From 5223767d93bb8e7fd2398a77ee27aa8bd374736c Mon Sep 17 00:00:00 2001 From: Zeal Caiden Date: Fri, 2 Oct 2026 20:02:15 -0700 Subject: [PATCH 1/4] arcmira 0.4.0: regenerate the Python SDK from the cleaned-up v1 document The SDK is regenerated from the v1 OpenAPI of 2026-10-02 (36 operations, down from 88). Methods whose operations left the document are gone, including the /v1/team routes deleted upstream, the slug families, corrections, transcripts.request and transcripts.status. transcripts.search keeps its name and now calls GET /v1/search. fern/method-names.json is keyed by operationId, and prepare-openapi.py is shared byte for byte with the TypeScript repo: it fails on an operation without a name and on a name for an operation the document lacks, keeps ResolveSuggestion nullable (resolve answers suggested: null beside best), and builds the TranscriptResult union from ready (200) and pending (202). Premium is one read, so prepare_and_wait and its errors are deleted. Reads take ids, dates are after and before, bodies are snake_case and lists name their collection; CHANGELOG.md lists every change with its replacement. --- CHANGELOG.md | 59 + README.md | 150 +- VERSION | 2 +- fern/method-names.json | 487 +- fern/openapi.json | 21221 +++------------- llms.txt | 49 +- reference.md | 6693 +---- scripts/install-generated.py | 13 +- scripts/overrides/prepare.py | 179 - scripts/prepare-openapi.py | 146 +- src/arcmira/__init__.py | 884 +- src/arcmira/_package.py | 2 +- src/arcmira/channels/__init__.py | 66 +- src/arcmira/channels/client.py | 109 - src/arcmira/channels/guests/__init__.py | 38 - src/arcmira/channels/guests/client.py | 218 - src/arcmira/channels/guests/raw_client.py | 397 - src/arcmira/channels/guests/types/__init__.py | 40 - .../list_guests_request_is_appearance.py | 5 - .../guests/types/list_guests_request_mode.py | 5 - .../guests/types/list_guests_request_order.py | 5 - src/arcmira/channels/raw_client.py | 242 - src/arcmira/channels/related/__init__.py | 82 - src/arcmira/channels/related/client.py | 930 - src/arcmira/channels/related/raw_client.py | 1861 -- .../channels/related/types/__init__.py | 80 - .../channels_related_request_is_appearance.py | 5 - .../types/channels_related_request_mode.py | 5 - .../types/channels_related_request_order.py | 5 - ...nizations_related_request_is_appearance.py | 5 - .../organizations_related_request_mode.py | 5 - .../organizations_related_request_order.py | 5 - .../people_related_request_is_appearance.py | 5 - .../types/people_related_request_mode.py | 5 - .../types/people_related_request_order.py | 5 - .../products_related_request_is_appearance.py | 5 - .../types/products_related_request_mode.py | 5 - .../types/products_related_request_order.py | 5 - .../topics_related_request_is_appearance.py | 5 - .../types/topics_related_request_mode.py | 5 - .../types/topics_related_request_order.py | 5 - src/arcmira/channels/videos/client.py | 38 +- src/arcmira/channels/videos/raw_client.py | 40 +- src/arcmira/client.py | 121 +- src/arcmira/core/client_wrapper.py | 2 +- src/arcmira/corrections/__init__.py | 37 - src/arcmira/corrections/client.py | 406 - src/arcmira/corrections/raw_client.py | 1024 - src/arcmira/corrections/types/__init__.py | 38 - .../submit_corrections_request_anchor.py | 38 - .../types/submit_corrections_request_kind.py | 8 - src/arcmira/entities/__init__.py | 27 +- src/arcmira/entities/client.py | 313 - src/arcmira/entities/mentions/client.py | 221 - .../entities/mentions/types/__init__.py | 38 - .../types/list_mentions_request_sentiment.py | 5 - src/arcmira/entities/raw_client.py | 903 +- .../entities/recommendations/client.py | 212 - .../entities/recommendations/raw_client.py | 393 - .../recommendations/types/__init__.py | 36 - ...t_recommendations_request_mention_class.py | 7 - src/arcmira/entities/types/__init__.py | 10 +- .../types/lookup_entities_request_type.py | 7 - .../types/search_entities_request_type.py | 7 - src/arcmira/errors/__init__.py | 3 - .../errors/precondition_failed_error.py | 11 - src/arcmira/feedback/__init__.py | 9 +- src/arcmira/feedback/client.py | 59 +- src/arcmira/feedback/raw_client.py | 53 +- src/arcmira/feedback/types/__init__.py | 9 +- .../types/submit_feedback_request_category.py | 7 + ...ubmit_feedback_request_corrections_item.py | 17 +- ...feedback_request_corrections_item_class.py | 5 + ..._request_corrections_item_mention_class.py | 7 - .../types/submit_feedback_request_type.py | 1 + .../{team => integrations}/__init__.py | 6 +- src/arcmira/integrations/client.py | 63 + src/arcmira/integrations/raw_client.py | 13 + .../slack}/__init__.py | 0 src/arcmira/integrations/slack/client.py | 100 + .../slack}/raw_client.py | 94 +- src/arcmira/mentions/__init__.py | 15 +- src/arcmira/mentions/client.py | 121 +- src/arcmira/mentions/raw_client.py | 131 +- src/arcmira/mentions/types/__init__.py | 9 +- .../list_mentions_request_entity_type.py | 7 - src/arcmira/monitors/__init__.py | 14 +- src/arcmira/monitors/client.py | 85 +- .../entities}/__init__.py | 6 +- src/arcmira/monitors/entities/client.py | 164 + .../entities}/raw_client.py | 234 +- .../entities/types}/__init__.py | 7 +- .../add_entities_request_person_match_mode.py | 5 + src/arcmira/monitors/raw_client.py | 138 +- src/arcmira/monitors/trackers/client.py | 8 +- src/arcmira/monitors/trackers/raw_client.py | 8 +- src/arcmira/organizations/__init__.py | 85 - src/arcmira/organizations/client.py | 133 - src/arcmira/organizations/raw_client.py | 268 - src/arcmira/organizations/related/__init__.py | 82 - src/arcmira/organizations/related/client.py | 930 - .../organizations/related/raw_client.py | 1861 -- .../organizations/related/types/__init__.py | 80 - .../channels_related_request_is_appearance.py | 5 - .../types/channels_related_request_mode.py | 5 - .../types/channels_related_request_order.py | 5 - ...nizations_related_request_is_appearance.py | 5 - .../organizations_related_request_mode.py | 5 - .../organizations_related_request_order.py | 5 - .../people_related_request_is_appearance.py | 5 - .../types/people_related_request_mode.py | 5 - .../types/people_related_request_order.py | 5 - .../products_related_request_is_appearance.py | 5 - .../types/products_related_request_mode.py | 5 - .../types/products_related_request_order.py | 5 - .../topics_related_request_is_appearance.py | 5 - .../types/topics_related_request_mode.py | 5 - .../types/topics_related_request_order.py | 5 - src/arcmira/people/__init__.py | 94 - src/arcmira/people/appearances/__init__.py | 38 - src/arcmira/people/appearances/client.py | 218 - src/arcmira/people/appearances/raw_client.py | 397 - .../people/appearances/types/__init__.py | 40 - .../list_appearances_request_is_appearance.py | 5 - .../types/list_appearances_request_mode.py | 5 - .../types/list_appearances_request_order.py | 5 - src/arcmira/people/client.py | 150 - src/arcmira/people/raw_client.py | 268 - src/arcmira/people/related/__init__.py | 82 - src/arcmira/people/related/client.py | 930 - src/arcmira/people/related/raw_client.py | 1861 -- src/arcmira/people/related/types/__init__.py | 80 - .../channels_related_request_is_appearance.py | 5 - .../types/channels_related_request_mode.py | 5 - .../types/channels_related_request_order.py | 5 - ...nizations_related_request_is_appearance.py | 5 - .../organizations_related_request_mode.py | 5 - .../organizations_related_request_order.py | 5 - .../people_related_request_is_appearance.py | 5 - .../types/people_related_request_mode.py | 5 - .../types/people_related_request_order.py | 5 - .../products_related_request_is_appearance.py | 5 - .../types/products_related_request_mode.py | 5 - .../types/products_related_request_order.py | 5 - .../topics_related_request_is_appearance.py | 5 - .../types/topics_related_request_mode.py | 5 - .../types/topics_related_request_order.py | 5 - src/arcmira/products/__init__.py | 85 - src/arcmira/products/client.py | 131 - src/arcmira/products/raw_client.py | 268 - src/arcmira/products/related/__init__.py | 82 - src/arcmira/products/related/client.py | 930 - src/arcmira/products/related/raw_client.py | 1861 -- .../products/related/types/__init__.py | 80 - .../channels_related_request_is_appearance.py | 5 - .../types/channels_related_request_mode.py | 5 - .../types/channels_related_request_order.py | 5 - ...nizations_related_request_is_appearance.py | 5 - .../organizations_related_request_mode.py | 5 - .../organizations_related_request_order.py | 5 - .../people_related_request_is_appearance.py | 5 - .../types/people_related_request_mode.py | 5 - .../types/people_related_request_order.py | 5 - .../products_related_request_is_appearance.py | 5 - .../types/products_related_request_mode.py | 5 - .../types/products_related_request_order.py | 5 - .../topics_related_request_is_appearance.py | 5 - .../types/topics_related_request_mode.py | 5 - .../types/topics_related_request_order.py | 5 - src/arcmira/recommendations/__init__.py | 9 +- src/arcmira/recommendations/client.py | 101 +- src/arcmira/recommendations/raw_client.py | 115 +- src/arcmira/recommendations/types/__init__.py | 10 +- .../list_recommendations_request_class.py | 5 + ...ist_recommendations_request_entity_type.py | 7 - ...t_recommendations_request_mention_class.py | 7 - src/arcmira/team/client.py | 186 - src/arcmira/team/raw_client.py | 451 - src/arcmira/team/usage_events/client.py | 143 - src/arcmira/team/usage_events/raw_client.py | 302 - src/arcmira/topics/__init__.py | 85 - src/arcmira/topics/client.py | 131 - src/arcmira/topics/related/__init__.py | 82 - src/arcmira/topics/related/client.py | 930 - src/arcmira/topics/related/raw_client.py | 1861 -- src/arcmira/topics/related/types/__init__.py | 80 - .../channels_related_request_is_appearance.py | 5 - .../types/channels_related_request_mode.py | 5 - .../types/channels_related_request_order.py | 5 - ...nizations_related_request_is_appearance.py | 5 - .../organizations_related_request_mode.py | 5 - .../organizations_related_request_order.py | 5 - .../people_related_request_is_appearance.py | 5 - .../types/people_related_request_mode.py | 5 - .../types/people_related_request_order.py | 5 - .../products_related_request_is_appearance.py | 5 - .../types/products_related_request_mode.py | 5 - .../types/products_related_request_order.py | 5 - .../topics_related_request_is_appearance.py | 5 - .../types/topics_related_request_mode.py | 5 - .../types/topics_related_request_order.py | 5 - src/arcmira/trackers/client.py | 20 +- src/arcmira/trackers/raw_client.py | 88 +- src/arcmira/transcripts/__init__.py | 6 +- src/arcmira/transcripts/client.py | 399 +- src/arcmira/transcripts/edits/__init__.py | 3 - src/arcmira/transcripts/edits/client.py | 262 - src/arcmira/transcripts/edits/raw_client.py | 560 - src/arcmira/transcripts/merges/__init__.py | 3 - src/arcmira/transcripts/merges/client.py | 331 - src/arcmira/transcripts/merges/raw_client.py | 777 - src/arcmira/transcripts/prepare.py | 179 - src/arcmira/transcripts/raw_client.py | 1201 +- src/arcmira/transcripts/speakers/__init__.py | 3 - src/arcmira/transcripts/speakers/client.py | 266 - .../transcripts/speakers/raw_client.py | 568 - src/arcmira/types/__init__.py | 852 +- src/arcmira/types/alert.py | 9 +- src/arcmira/types/alert_list_response.py | 2 +- .../types/channel_guest_list_response.py | 61 - ...guest_list_response_export_capabilities.py | 69 - .../channel_guest_list_response_items_item.py | 60 - ...uest_list_response_items_item_sentiment.py | 5 - src/arcmira/types/channel_page_response.py | 103 - .../channel_page_response_channel_info.py | 72 - .../types/channel_page_response_entity.py | 122 - .../channel_page_response_entity_owner.py | 41 - .../channel_page_response_entity_type.py | 5 - ...el_page_response_episodes_by_month_item.py | 36 - .../channel_page_response_episodes_item.py | 130 - ...el_page_response_episodes_item_platform.py | 5 - ...l_page_response_episodes_item_sentiment.py | 5 - ...l_page_response_episodes_item_timestamp.py | 5 - ...hannel_page_response_episodes_item_type.py | 5 - .../channel_page_response_guests_item.py | 61 - .../channel_page_response_guests_item_role.py | 5 - ...nel_page_response_guests_item_sentiment.py | 5 - ...annel_page_response_hosts_detailed_item.py | 60 - ..._response_hosts_detailed_item_sentiment.py | 5 - ...hannel_page_response_organizations_item.py | 55 - ...e_response_organizations_item_sentiment.py | 7 - .../channel_page_response_products_item.py | 55 - ...l_page_response_products_item_sentiment.py | 5 - .../channel_page_response_topics_item.py | 33 - ...nel_page_response_topics_item_sentiment.py | 5 - src/arcmira/types/channel_sponsor.py | 8 +- src/arcmira/types/channel_sponsor_entity.py | 46 - .../types/channel_sponsors_response_access.py | 6 + ...hannel_sponsors_response_access_details.py | 37 + ..._sponsors_response_access_details_quote.py | 33 + ...rs_response_access_details_quote_charge.py | 40 + ...sponse_access_details_quote_charge_from.py | 7 + ...sponse_access_details_quote_charge_unit.py | 5 + src/arcmira/types/channel_videos_response.py | 2 + .../types/correction_accepted_response.py | 33 - .../correction_accepted_response_kind.py | 8 - src/arcmira/types/entity.py | 5 - src/arcmira/types/entity_card.py | 77 - src/arcmira/types/entity_cards_response.py | 23 - .../types/entity_channel_list_response.py | 61 - ...annel_list_response_export_capabilities.py | 69 - ...entity_channel_list_response_items_item.py | 54 - src/arcmira/types/entity_lookup_response.py | 20 - .../types/entity_momentum_response_access.py | 6 + ...entity_momentum_response_access_details.py | 37 + ..._momentum_response_access_details_quote.py | 33 + ...um_response_access_details_quote_charge.py | 40 + ...sponse_access_details_quote_charge_from.py | 7 + ...sponse_access_details_quote_charge_unit.py | 5 + .../entity_organization_list_response.py | 61 - ...ation_list_response_export_capabilities.py | 69 - ...y_organization_list_response_items_item.py | 65 - ...tion_list_response_items_item_sentiment.py | 7 - src/arcmira/types/entity_page_mention.py | 158 - .../types/entity_page_mention_excerpt.py | 106 - ...age_mention_excerpt_public_source_class.py | 7 - .../types/entity_page_mention_platform.py | 5 - .../types/entity_page_mention_sentiment.py | 5 - src/arcmira/types/entity_page_mention_type.py | 5 - .../types/entity_people_list_response.py | 74 - ...eople_list_response_export_capabilities.py | 69 - .../entity_people_list_response_items_item.py | 60 - ...ople_list_response_items_item_sentiment.py | 5 - ...entity_people_list_response_people_mode.py | 5 - .../types/entity_product_list_response.py | 61 - ...oduct_list_response_export_capabilities.py | 69 - ...entity_product_list_response_items_item.py | 77 - ...duct_list_response_items_item_sentiment.py | 7 - src/arcmira/types/entity_resolve_response.py | 2 +- src/arcmira/types/entity_search_response.py | 29 - src/arcmira/types/entity_search_result.py | 73 - ...y_search_result_recommendations_summary.py | 36 - .../types/entity_topic_list_response.py | 61 - ...topic_list_response_export_capabilities.py | 69 - .../entity_topic_list_response_items_item.py | 38 - ...opic_list_response_items_item_sentiment.py | 5 - src/arcmira/types/error.py | 25 - src/arcmira/types/error_error.py | 6 + src/arcmira/types/error_error_details.py | 37 + ..._quote.py => error_error_details_quote.py} | 6 +- ...py => error_error_details_quote_charge.py} | 10 +- .../error_error_details_quote_charge_from.py | 5 + .../error_error_details_quote_charge_unit.py | 5 + src/arcmira/types/error_quote_charge_from.py | 5 - src/arcmira/types/error_quote_charge_unit.py | 5 - src/arcmira/types/exposure_meta.py | 313 - src/arcmira/types/exposure_meta_access.py | 92 - .../types/exposure_meta_access_chart.py | 26 - src/arcmira/types/exposure_meta_access_cls.py | 5 - .../types/exposure_meta_access_freshness.py | 37 - .../types/exposure_meta_access_ladder.py | 5 - .../types/exposure_meta_access_rows.py | 39 - .../exposure_meta_access_rows_entities.py | 36 - .../types/exposure_meta_access_rows_media.py | 36 - .../types/exposure_meta_access_rows_topics.py | 36 - .../types/exposure_meta_access_unlock.py | 51 - ...xposure_meta_access_unlock_limit_action.py | 15 - .../types/exposure_meta_access_unlock_src.py | 5 - .../types/exposure_meta_access_view.py | 5 - .../exposure_meta_access_withheld_item.py | 68 - ...exposure_meta_access_withheld_item_kind.py | 7 - ...xposure_meta_access_withheld_item_param.py | 5 - ...osure_meta_access_withheld_item_section.py | 5 - ...exposure_meta_access_withheld_item_what.py | 7 - src/arcmira/types/exposure_meta_credits.py | 41 - .../types/exposure_meta_credits_on_demand.py | 32 - .../types/exposure_meta_credits_plan.py | 32 - .../types/exposure_meta_limit_action.py | 15 - src/arcmira/types/exposure_meta_limits.py | 70 - .../types/exposure_meta_recent_preview.py | 89 - ...exposure_meta_recent_preview_experiment.py | 5 - .../exposure_meta_recent_preview_mentions.py | 89 - ...meta_recent_preview_mentions_experiment.py | 5 - ...re_meta_recent_preview_mentions_subject.py | 5 - ...cent_preview_mentions_teaser_items_item.py | 54 - .../exposure_meta_recent_preview_subject.py | 5 - ...e_meta_recent_preview_teaser_items_item.py | 54 - src/arcmira/types/exposure_meta_totals.py | 71 - .../types/exposure_meta_usage_limit_type.py | 5 - .../types/feedback_correction_result.py | 21 - ...edback_correction_result_recommendation.py | 105 - ..._correction_result_recommendation_media.py | 47 - .../types/feedback_readback_correction.py | 2 +- .../types/feedback_readback_response.py | 6 +- src/arcmira/types/feedback_response.py | 8 +- src/arcmira/types/me_response.py | 4 +- src/arcmira/types/mention.py | 19 +- src/arcmira/types/mention_counts_response.py | 22 +- src/arcmira/types/mention_list_response.py | 16 +- .../types/mention_list_response_entity.py | 111 - src/arcmira/types/mention_media.py | 5 - src/arcmira/types/mention_recommendations.py | 2 +- src/arcmira/types/merge_suggestion_change.py | 32 +- src/arcmira/types/monitor.py | 187 +- .../monitor_access.py} | 2 +- ...se.py => monitor_add_entities_response.py} | 12 +- .../types/monitor_add_trackers_response.py | 14 +- src/arcmira/types/monitor_delete_response.py | 10 +- .../types/monitor_email_recipients_item.py | 25 +- .../monitor_email_recipients_item_role.py | 5 + .../monitor_email_recipients_item_status.py | 4 +- src/arcmira/types/monitor_entity_result.py | 53 + .../types/monitor_entity_result_reason.py | 10 + src/arcmira/types/monitor_list_response.py | 2 +- .../monitor_list_response_monitors_item.py | 26 +- .../monitor_mutation_response_monitor.py | 21 +- ...mbers_response_team.py => monitor_team.py} | 6 +- ...monitor_trackers_response_trackers_item.py | 58 +- .../types/organization_page_response.py | 89 - ...rganization_page_response_channels_item.py | 60 - ...n_page_response_channels_item_sentiment.py | 7 - .../organization_page_response_entity.py | 99 - ...age_response_entity_owned_channels_item.py | 82 - ...age_response_entity_owned_products_item.py | 82 - .../organization_page_response_entity_type.py | 5 - ...on_page_response_mentions_by_month_item.py | 36 - .../organization_page_response_people_item.py | 55 - ...ion_page_response_people_item_sentiment.py | 7 - ...rganization_page_response_products_item.py | 55 - ...n_page_response_products_item_sentiment.py | 5 - .../organization_page_response_role_edge.py | 137 - ...anization_page_response_role_edge_label.py | 5 - ...ion_page_response_role_edge_people_item.py | 63 - ...ponse_role_edge_recent_appearances_item.py | 61 - ...ganization_page_response_role_edge_role.py | 5 - .../types/organization_page_response_stats.py | 84 - .../organization_page_response_topics_item.py | 33 - ...ion_page_response_topics_item_sentiment.py | 7 - .../types/person_appearance_list_response.py | 48 - ...son_appearance_list_response_items_item.py | 134 - ...rance_list_response_items_item_platform.py | 5 - ...ance_list_response_items_item_sentiment.py | 7 - ...ppearance_list_response_items_item_type.py | 5 - src/arcmira/types/person_page_response.py | 106 - ...page_response_appearances_by_month_item.py | 36 - .../person_page_response_appearances_item.py | 134 - ...page_response_appearances_item_platform.py | 5 - ...age_response_appearances_item_sentiment.py | 5 - ...son_page_response_appearances_item_type.py | 5 - .../types/person_page_response_brands_item.py | 55 - ...son_page_response_brands_item_sentiment.py | 5 - .../types/person_page_response_entity.py | 132 - ...age_response_entity_owned_channels_item.py | 82 - ...age_response_entity_owned_products_item.py | 82 - ...on_page_response_mentions_by_month_item.py | 36 - .../person_page_response_mentions_item.py | 134 - ...on_page_response_mentions_item_platform.py | 5 - ...n_page_response_mentions_item_sentiment.py | 5 - ...person_page_response_mentions_item_type.py | 5 - .../types/person_page_response_people_item.py | 61 - .../person_page_response_people_item_role.py | 5 - ...son_page_response_people_item_sentiment.py | 5 - .../person_page_response_products_item.py | 55 - ...n_page_response_products_item_sentiment.py | 5 - .../types/person_page_response_role_edge.py | 71 - .../person_page_response_role_edge_label.py | 5 - .../person_page_response_role_edge_role.py | 5 - .../types/person_page_response_stats.py | 91 - .../types/person_page_response_topics_item.py | 33 - ...son_page_response_topics_item_sentiment.py | 5 - src/arcmira/types/product_page_response.py | 83 - .../product_page_response_channels_item.py | 60 - ...t_page_response_channels_item_sentiment.py | 5 - .../types/product_page_response_entity.py | 92 - .../product_page_response_entity_owner.py | 41 - ...product_page_response_entity_parent_org.py | 31 - .../product_page_response_entity_type.py | 5 - ...ct_page_response_mentions_by_month_item.py | 36 - .../product_page_response_opportunities.py | 37 - ...roduct_page_response_organizations_item.py | 55 - ...e_response_organizations_item_sentiment.py | 7 - .../product_page_response_people_item.py | 55 - ...uct_page_response_people_item_sentiment.py | 5 - .../types/product_page_response_stats.py | 84 - .../product_page_response_topics_item.py | 33 - ...uct_page_response_topics_item_sentiment.py | 5 - ...esponse_stats.py => publication_window.py} | 22 +- src/arcmira/types/published_excerpt.py | 106 - .../published_excerpt_public_source_class.py | 7 - src/arcmira/types/recommendation.py | 33 +- src/arcmira/types/recommendation_class.py | 5 + .../types/recommendation_enrichment_item.py | 28 +- .../recommendation_enrichment_item_class.py | 5 + .../types/recommendation_list_response.py | 15 +- .../recommendation_list_response_entity.py | 111 - src/arcmira/types/search_resolve_response.py | 46 - .../types/search_resolve_response_entity.py | 36 - ....py => slack_integration_list_response.py} | 16 +- ...gration_list_response_integrations_item.py | 40 + ...sponse_integrations_item_channels_item.py} | 10 +- ...eaker_identification_submitted_response.py | 25 - ...ation_submitted_response_identification.py | 64 - ...ubmitted_response_identification_entity.py | 36 - ...ubmitted_response_identification_status.py | 7 - src/arcmira/types/team_member.py | 49 - src/arcmira/types/team_member_role.py | 5 - src/arcmira/types/team_member_seat_type.py | 5 - src/arcmira/types/team_member_spend.py | 59 - src/arcmira/types/team_member_spend_role.py | 5 - .../types/team_member_spend_seat_type.py | 5 - src/arcmira/types/team_members_response.py | 26 - src/arcmira/types/team_usage_event.py | 67 - .../types/team_usage_events_response.py | 33 - src/arcmira/types/topic_page_response.py | 87 - .../topic_page_response_channels_item.py | 60 - ...c_page_response_channels_item_sentiment.py | 5 - .../topic_page_response_companies_item.py | 55 - ..._page_response_companies_item_sentiment.py | 5 - .../types/topic_page_response_entity.py | 68 - .../types/topic_page_response_entity_type.py | 5 - ...ic_page_response_mentions_by_month_item.py | 36 - .../topic_page_response_products_item.py | 55 - ...c_page_response_products_item_sentiment.py | 5 - ...topic_page_response_related_topics_item.py | 33 - ..._response_related_topics_item_sentiment.py | 7 - .../types/topic_page_response_stats.py | 94 - .../types/topic_page_response_voices_item.py | 61 - .../topic_page_response_voices_item_role.py | 5 - ...pic_page_response_voices_item_sentiment.py | 5 - src/arcmira/types/tracker.py | 134 +- .../transcript_edit_submitted_response.py | 23 - ...transcript_edit_submitted_response_edit.py | 61 - ...ipt_edit_submitted_response_edit_status.py | 7 - .../types/transcript_preparation_required.py | 38 - .../transcript_preparation_required_action.py | 27 - ...script_preparation_required_action_body.py | 19 - ...ript_preparation_required_action_method.py | 5 - ...cript_preparation_required_last_attempt.py | 24 - ...transcript_preparation_required_quality.py | 5 - .../transcript_preparation_required_quote.py | 38 - ...cript_preparation_required_quote_charge.py | 36 - ..._preparation_required_quote_charge_unit.py | 5 - .../transcript_request_submit_response.py | 24 - .../types/transcript_response_access.py | 6 + .../transcript_response_access_details.py | 37 + ...ranscript_response_access_details_quote.py | 33 + ...pt_response_access_details_quote_charge.py | 36 + ...ponse_access_details_quote_charge_from.py} | 2 +- ...sponse_access_details_quote_charge_unit.py | 5 + .../transcript_response_speakers_item.py | 4 +- src/arcmira/types/transcript_result.py | 25 +- src/arcmira/types/transcript_search_chunk.py | 79 +- .../types/transcript_search_response.py | 26 +- .../transcript_search_response_access.py | 6 + ...anscript_search_response_access_details.py | 37 + ...pt_search_response_access_details_quote.py | 33 + ...ch_response_access_details_quote_charge.py | 40 + ...sponse_access_details_quote_charge_from.py | 7 + ...sponse_access_details_quote_charge_unit.py | 5 + .../transcript_search_response_filters.py | 30 +- ...cript_search_response_filters_kind_item.py | 5 + ...y => transcript_search_response_unlock.py} | 12 +- src/arcmira/types/video_captions_response.py | 25 - .../types/video_merge_list_response.py | 23 - .../video_merge_list_response_merges_item.py | 84 - ..._merge_list_response_merges_item_status.py | 7 - .../types/video_merge_submitted_response.py | 23 - .../video_merge_submitted_response_merge.py | 70 - ...o_merge_submitted_response_merge_status.py | 7 - .../types/webhook_secret_rotate_response.py | 29 +- src/arcmira/types/withdrawn_response.py | 22 - .../types/wrong_classification_change.py | 15 +- .../wrong_classification_change_class.py | 5 + ...ong_classification_change_mention_class.py | 5 - tests/fixtures/transcription-responses.json | 227 +- tests/test_generation.py | 114 +- tests/test_prepare.py | 225 - tests/test_transcription.py | 283 +- 528 files changed, 7815 insertions(+), 64633 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 scripts/overrides/prepare.py delete mode 100644 src/arcmira/channels/guests/__init__.py delete mode 100644 src/arcmira/channels/guests/client.py delete mode 100644 src/arcmira/channels/guests/raw_client.py delete mode 100644 src/arcmira/channels/guests/types/__init__.py delete mode 100644 src/arcmira/channels/guests/types/list_guests_request_is_appearance.py delete mode 100644 src/arcmira/channels/guests/types/list_guests_request_mode.py delete mode 100644 src/arcmira/channels/guests/types/list_guests_request_order.py delete mode 100644 src/arcmira/channels/related/__init__.py delete mode 100644 src/arcmira/channels/related/client.py delete mode 100644 src/arcmira/channels/related/raw_client.py delete mode 100644 src/arcmira/channels/related/types/__init__.py delete mode 100644 src/arcmira/channels/related/types/channels_related_request_is_appearance.py delete mode 100644 src/arcmira/channels/related/types/channels_related_request_mode.py delete mode 100644 src/arcmira/channels/related/types/channels_related_request_order.py delete mode 100644 src/arcmira/channels/related/types/organizations_related_request_is_appearance.py delete mode 100644 src/arcmira/channels/related/types/organizations_related_request_mode.py delete mode 100644 src/arcmira/channels/related/types/organizations_related_request_order.py delete mode 100644 src/arcmira/channels/related/types/people_related_request_is_appearance.py delete mode 100644 src/arcmira/channels/related/types/people_related_request_mode.py delete mode 100644 src/arcmira/channels/related/types/people_related_request_order.py delete mode 100644 src/arcmira/channels/related/types/products_related_request_is_appearance.py delete mode 100644 src/arcmira/channels/related/types/products_related_request_mode.py delete mode 100644 src/arcmira/channels/related/types/products_related_request_order.py delete mode 100644 src/arcmira/channels/related/types/topics_related_request_is_appearance.py delete mode 100644 src/arcmira/channels/related/types/topics_related_request_mode.py delete mode 100644 src/arcmira/channels/related/types/topics_related_request_order.py delete mode 100644 src/arcmira/corrections/__init__.py delete mode 100644 src/arcmira/corrections/client.py delete mode 100644 src/arcmira/corrections/raw_client.py delete mode 100644 src/arcmira/corrections/types/__init__.py delete mode 100644 src/arcmira/corrections/types/submit_corrections_request_anchor.py delete mode 100644 src/arcmira/corrections/types/submit_corrections_request_kind.py delete mode 100644 src/arcmira/entities/mentions/client.py delete mode 100644 src/arcmira/entities/mentions/types/__init__.py delete mode 100644 src/arcmira/entities/mentions/types/list_mentions_request_sentiment.py delete mode 100644 src/arcmira/entities/recommendations/client.py delete mode 100644 src/arcmira/entities/recommendations/raw_client.py delete mode 100644 src/arcmira/entities/recommendations/types/__init__.py delete mode 100644 src/arcmira/entities/recommendations/types/list_recommendations_request_mention_class.py delete mode 100644 src/arcmira/entities/types/lookup_entities_request_type.py delete mode 100644 src/arcmira/entities/types/search_entities_request_type.py delete mode 100644 src/arcmira/errors/precondition_failed_error.py create mode 100644 src/arcmira/feedback/types/submit_feedback_request_category.py create mode 100644 src/arcmira/feedback/types/submit_feedback_request_corrections_item_class.py delete mode 100644 src/arcmira/feedback/types/submit_feedback_request_corrections_item_mention_class.py rename src/arcmira/{team => integrations}/__init__.py (87%) create mode 100644 src/arcmira/integrations/client.py create mode 100644 src/arcmira/integrations/raw_client.py rename src/arcmira/{team/usage_events => integrations/slack}/__init__.py (100%) create mode 100644 src/arcmira/integrations/slack/client.py rename src/arcmira/{topics => integrations/slack}/raw_client.py (74%) delete mode 100644 src/arcmira/mentions/types/list_mentions_request_entity_type.py rename src/arcmira/{entities/recommendations => monitors/entities}/__init__.py (81%) create mode 100644 src/arcmira/monitors/entities/client.py rename src/arcmira/{entities/mentions => monitors/entities}/raw_client.py (56%) rename src/arcmira/{entities/mentions => monitors/entities/types}/__init__.py (79%) create mode 100644 src/arcmira/monitors/entities/types/add_entities_request_person_match_mode.py delete mode 100644 src/arcmira/organizations/__init__.py delete mode 100644 src/arcmira/organizations/client.py delete mode 100644 src/arcmira/organizations/raw_client.py delete mode 100644 src/arcmira/organizations/related/__init__.py delete mode 100644 src/arcmira/organizations/related/client.py delete mode 100644 src/arcmira/organizations/related/raw_client.py delete mode 100644 src/arcmira/organizations/related/types/__init__.py delete mode 100644 src/arcmira/organizations/related/types/channels_related_request_is_appearance.py delete mode 100644 src/arcmira/organizations/related/types/channels_related_request_mode.py delete mode 100644 src/arcmira/organizations/related/types/channels_related_request_order.py delete mode 100644 src/arcmira/organizations/related/types/organizations_related_request_is_appearance.py delete mode 100644 src/arcmira/organizations/related/types/organizations_related_request_mode.py delete mode 100644 src/arcmira/organizations/related/types/organizations_related_request_order.py delete mode 100644 src/arcmira/organizations/related/types/people_related_request_is_appearance.py delete mode 100644 src/arcmira/organizations/related/types/people_related_request_mode.py delete mode 100644 src/arcmira/organizations/related/types/people_related_request_order.py delete mode 100644 src/arcmira/organizations/related/types/products_related_request_is_appearance.py delete mode 100644 src/arcmira/organizations/related/types/products_related_request_mode.py delete mode 100644 src/arcmira/organizations/related/types/products_related_request_order.py delete mode 100644 src/arcmira/organizations/related/types/topics_related_request_is_appearance.py delete mode 100644 src/arcmira/organizations/related/types/topics_related_request_mode.py delete mode 100644 src/arcmira/organizations/related/types/topics_related_request_order.py delete mode 100644 src/arcmira/people/__init__.py delete mode 100644 src/arcmira/people/appearances/__init__.py delete mode 100644 src/arcmira/people/appearances/client.py delete mode 100644 src/arcmira/people/appearances/raw_client.py delete mode 100644 src/arcmira/people/appearances/types/__init__.py delete mode 100644 src/arcmira/people/appearances/types/list_appearances_request_is_appearance.py delete mode 100644 src/arcmira/people/appearances/types/list_appearances_request_mode.py delete mode 100644 src/arcmira/people/appearances/types/list_appearances_request_order.py delete mode 100644 src/arcmira/people/client.py delete mode 100644 src/arcmira/people/raw_client.py delete mode 100644 src/arcmira/people/related/__init__.py delete mode 100644 src/arcmira/people/related/client.py delete mode 100644 src/arcmira/people/related/raw_client.py delete mode 100644 src/arcmira/people/related/types/__init__.py delete mode 100644 src/arcmira/people/related/types/channels_related_request_is_appearance.py delete mode 100644 src/arcmira/people/related/types/channels_related_request_mode.py delete mode 100644 src/arcmira/people/related/types/channels_related_request_order.py delete mode 100644 src/arcmira/people/related/types/organizations_related_request_is_appearance.py delete mode 100644 src/arcmira/people/related/types/organizations_related_request_mode.py delete mode 100644 src/arcmira/people/related/types/organizations_related_request_order.py delete mode 100644 src/arcmira/people/related/types/people_related_request_is_appearance.py delete mode 100644 src/arcmira/people/related/types/people_related_request_mode.py delete mode 100644 src/arcmira/people/related/types/people_related_request_order.py delete mode 100644 src/arcmira/people/related/types/products_related_request_is_appearance.py delete mode 100644 src/arcmira/people/related/types/products_related_request_mode.py delete mode 100644 src/arcmira/people/related/types/products_related_request_order.py delete mode 100644 src/arcmira/people/related/types/topics_related_request_is_appearance.py delete mode 100644 src/arcmira/people/related/types/topics_related_request_mode.py delete mode 100644 src/arcmira/people/related/types/topics_related_request_order.py delete mode 100644 src/arcmira/products/__init__.py delete mode 100644 src/arcmira/products/client.py delete mode 100644 src/arcmira/products/raw_client.py delete mode 100644 src/arcmira/products/related/__init__.py delete mode 100644 src/arcmira/products/related/client.py delete mode 100644 src/arcmira/products/related/raw_client.py delete mode 100644 src/arcmira/products/related/types/__init__.py delete mode 100644 src/arcmira/products/related/types/channels_related_request_is_appearance.py delete mode 100644 src/arcmira/products/related/types/channels_related_request_mode.py delete mode 100644 src/arcmira/products/related/types/channels_related_request_order.py delete mode 100644 src/arcmira/products/related/types/organizations_related_request_is_appearance.py delete mode 100644 src/arcmira/products/related/types/organizations_related_request_mode.py delete mode 100644 src/arcmira/products/related/types/organizations_related_request_order.py delete mode 100644 src/arcmira/products/related/types/people_related_request_is_appearance.py delete mode 100644 src/arcmira/products/related/types/people_related_request_mode.py delete mode 100644 src/arcmira/products/related/types/people_related_request_order.py delete mode 100644 src/arcmira/products/related/types/products_related_request_is_appearance.py delete mode 100644 src/arcmira/products/related/types/products_related_request_mode.py delete mode 100644 src/arcmira/products/related/types/products_related_request_order.py delete mode 100644 src/arcmira/products/related/types/topics_related_request_is_appearance.py delete mode 100644 src/arcmira/products/related/types/topics_related_request_mode.py delete mode 100644 src/arcmira/products/related/types/topics_related_request_order.py create mode 100644 src/arcmira/recommendations/types/list_recommendations_request_class.py delete mode 100644 src/arcmira/recommendations/types/list_recommendations_request_entity_type.py delete mode 100644 src/arcmira/recommendations/types/list_recommendations_request_mention_class.py delete mode 100644 src/arcmira/team/client.py delete mode 100644 src/arcmira/team/raw_client.py delete mode 100644 src/arcmira/team/usage_events/client.py delete mode 100644 src/arcmira/team/usage_events/raw_client.py delete mode 100644 src/arcmira/topics/__init__.py delete mode 100644 src/arcmira/topics/client.py delete mode 100644 src/arcmira/topics/related/__init__.py delete mode 100644 src/arcmira/topics/related/client.py delete mode 100644 src/arcmira/topics/related/raw_client.py delete mode 100644 src/arcmira/topics/related/types/__init__.py delete mode 100644 src/arcmira/topics/related/types/channels_related_request_is_appearance.py delete mode 100644 src/arcmira/topics/related/types/channels_related_request_mode.py delete mode 100644 src/arcmira/topics/related/types/channels_related_request_order.py delete mode 100644 src/arcmira/topics/related/types/organizations_related_request_is_appearance.py delete mode 100644 src/arcmira/topics/related/types/organizations_related_request_mode.py delete mode 100644 src/arcmira/topics/related/types/organizations_related_request_order.py delete mode 100644 src/arcmira/topics/related/types/people_related_request_is_appearance.py delete mode 100644 src/arcmira/topics/related/types/people_related_request_mode.py delete mode 100644 src/arcmira/topics/related/types/people_related_request_order.py delete mode 100644 src/arcmira/topics/related/types/products_related_request_is_appearance.py delete mode 100644 src/arcmira/topics/related/types/products_related_request_mode.py delete mode 100644 src/arcmira/topics/related/types/products_related_request_order.py delete mode 100644 src/arcmira/topics/related/types/topics_related_request_is_appearance.py delete mode 100644 src/arcmira/topics/related/types/topics_related_request_mode.py delete mode 100644 src/arcmira/topics/related/types/topics_related_request_order.py delete mode 100644 src/arcmira/transcripts/edits/__init__.py delete mode 100644 src/arcmira/transcripts/edits/client.py delete mode 100644 src/arcmira/transcripts/edits/raw_client.py delete mode 100644 src/arcmira/transcripts/merges/__init__.py delete mode 100644 src/arcmira/transcripts/merges/client.py delete mode 100644 src/arcmira/transcripts/merges/raw_client.py delete mode 100644 src/arcmira/transcripts/prepare.py delete mode 100644 src/arcmira/transcripts/speakers/__init__.py delete mode 100644 src/arcmira/transcripts/speakers/client.py delete mode 100644 src/arcmira/transcripts/speakers/raw_client.py delete mode 100644 src/arcmira/types/channel_guest_list_response.py delete mode 100644 src/arcmira/types/channel_guest_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/channel_guest_list_response_items_item.py delete mode 100644 src/arcmira/types/channel_guest_list_response_items_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response.py delete mode 100644 src/arcmira/types/channel_page_response_channel_info.py delete mode 100644 src/arcmira/types/channel_page_response_entity.py delete mode 100644 src/arcmira/types/channel_page_response_entity_owner.py delete mode 100644 src/arcmira/types/channel_page_response_entity_type.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_by_month_item.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_item.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_item_platform.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_item_timestamp.py delete mode 100644 src/arcmira/types/channel_page_response_episodes_item_type.py delete mode 100644 src/arcmira/types/channel_page_response_guests_item.py delete mode 100644 src/arcmira/types/channel_page_response_guests_item_role.py delete mode 100644 src/arcmira/types/channel_page_response_guests_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response_hosts_detailed_item.py delete mode 100644 src/arcmira/types/channel_page_response_hosts_detailed_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response_organizations_item.py delete mode 100644 src/arcmira/types/channel_page_response_organizations_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response_products_item.py delete mode 100644 src/arcmira/types/channel_page_response_products_item_sentiment.py delete mode 100644 src/arcmira/types/channel_page_response_topics_item.py delete mode 100644 src/arcmira/types/channel_page_response_topics_item_sentiment.py delete mode 100644 src/arcmira/types/channel_sponsor_entity.py create mode 100644 src/arcmira/types/channel_sponsors_response_access_details.py create mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote.py create mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py create mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py create mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/correction_accepted_response.py delete mode 100644 src/arcmira/types/correction_accepted_response_kind.py delete mode 100644 src/arcmira/types/entity_card.py delete mode 100644 src/arcmira/types/entity_cards_response.py delete mode 100644 src/arcmira/types/entity_channel_list_response.py delete mode 100644 src/arcmira/types/entity_channel_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/entity_channel_list_response_items_item.py delete mode 100644 src/arcmira/types/entity_lookup_response.py create mode 100644 src/arcmira/types/entity_momentum_response_access_details.py create mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote.py create mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge.py create mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py create mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/entity_organization_list_response.py delete mode 100644 src/arcmira/types/entity_organization_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/entity_organization_list_response_items_item.py delete mode 100644 src/arcmira/types/entity_organization_list_response_items_item_sentiment.py delete mode 100644 src/arcmira/types/entity_page_mention.py delete mode 100644 src/arcmira/types/entity_page_mention_excerpt.py delete mode 100644 src/arcmira/types/entity_page_mention_excerpt_public_source_class.py delete mode 100644 src/arcmira/types/entity_page_mention_platform.py delete mode 100644 src/arcmira/types/entity_page_mention_sentiment.py delete mode 100644 src/arcmira/types/entity_page_mention_type.py delete mode 100644 src/arcmira/types/entity_people_list_response.py delete mode 100644 src/arcmira/types/entity_people_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/entity_people_list_response_items_item.py delete mode 100644 src/arcmira/types/entity_people_list_response_items_item_sentiment.py delete mode 100644 src/arcmira/types/entity_people_list_response_people_mode.py delete mode 100644 src/arcmira/types/entity_product_list_response.py delete mode 100644 src/arcmira/types/entity_product_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/entity_product_list_response_items_item.py delete mode 100644 src/arcmira/types/entity_product_list_response_items_item_sentiment.py delete mode 100644 src/arcmira/types/entity_search_response.py delete mode 100644 src/arcmira/types/entity_search_result.py delete mode 100644 src/arcmira/types/entity_search_result_recommendations_summary.py delete mode 100644 src/arcmira/types/entity_topic_list_response.py delete mode 100644 src/arcmira/types/entity_topic_list_response_export_capabilities.py delete mode 100644 src/arcmira/types/entity_topic_list_response_items_item.py delete mode 100644 src/arcmira/types/entity_topic_list_response_items_item_sentiment.py create mode 100644 src/arcmira/types/error_error_details.py rename src/arcmira/types/{error_quote.py => error_error_details_quote.py} (82%) rename src/arcmira/types/{error_quote_charge.py => error_error_details_quote_charge.py} (75%) create mode 100644 src/arcmira/types/error_error_details_quote_charge_from.py create mode 100644 src/arcmira/types/error_error_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/error_quote_charge_from.py delete mode 100644 src/arcmira/types/error_quote_charge_unit.py delete mode 100644 src/arcmira/types/exposure_meta.py delete mode 100644 src/arcmira/types/exposure_meta_access.py delete mode 100644 src/arcmira/types/exposure_meta_access_chart.py delete mode 100644 src/arcmira/types/exposure_meta_access_cls.py delete mode 100644 src/arcmira/types/exposure_meta_access_freshness.py delete mode 100644 src/arcmira/types/exposure_meta_access_ladder.py delete mode 100644 src/arcmira/types/exposure_meta_access_rows.py delete mode 100644 src/arcmira/types/exposure_meta_access_rows_entities.py delete mode 100644 src/arcmira/types/exposure_meta_access_rows_media.py delete mode 100644 src/arcmira/types/exposure_meta_access_rows_topics.py delete mode 100644 src/arcmira/types/exposure_meta_access_unlock.py delete mode 100644 src/arcmira/types/exposure_meta_access_unlock_limit_action.py delete mode 100644 src/arcmira/types/exposure_meta_access_unlock_src.py delete mode 100644 src/arcmira/types/exposure_meta_access_view.py delete mode 100644 src/arcmira/types/exposure_meta_access_withheld_item.py delete mode 100644 src/arcmira/types/exposure_meta_access_withheld_item_kind.py delete mode 100644 src/arcmira/types/exposure_meta_access_withheld_item_param.py delete mode 100644 src/arcmira/types/exposure_meta_access_withheld_item_section.py delete mode 100644 src/arcmira/types/exposure_meta_access_withheld_item_what.py delete mode 100644 src/arcmira/types/exposure_meta_credits.py delete mode 100644 src/arcmira/types/exposure_meta_credits_on_demand.py delete mode 100644 src/arcmira/types/exposure_meta_credits_plan.py delete mode 100644 src/arcmira/types/exposure_meta_limit_action.py delete mode 100644 src/arcmira/types/exposure_meta_limits.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_experiment.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_mentions.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_mentions_experiment.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_mentions_subject.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_mentions_teaser_items_item.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_subject.py delete mode 100644 src/arcmira/types/exposure_meta_recent_preview_teaser_items_item.py delete mode 100644 src/arcmira/types/exposure_meta_totals.py delete mode 100644 src/arcmira/types/exposure_meta_usage_limit_type.py delete mode 100644 src/arcmira/types/feedback_correction_result_recommendation.py delete mode 100644 src/arcmira/types/feedback_correction_result_recommendation_media.py delete mode 100644 src/arcmira/types/mention_list_response_entity.py rename src/arcmira/{entities/mentions/types/list_mentions_request_details.py => types/monitor_access.py} (59%) rename src/arcmira/types/{team_spend_response.py => monitor_add_entities_response.py} (60%) create mode 100644 src/arcmira/types/monitor_email_recipients_item_role.py create mode 100644 src/arcmira/types/monitor_entity_result.py create mode 100644 src/arcmira/types/monitor_entity_result_reason.py rename src/arcmira/types/{team_members_response_team.py => monitor_team.py} (82%) delete mode 100644 src/arcmira/types/organization_page_response.py delete mode 100644 src/arcmira/types/organization_page_response_channels_item.py delete mode 100644 src/arcmira/types/organization_page_response_channels_item_sentiment.py delete mode 100644 src/arcmira/types/organization_page_response_entity.py delete mode 100644 src/arcmira/types/organization_page_response_entity_owned_channels_item.py delete mode 100644 src/arcmira/types/organization_page_response_entity_owned_products_item.py delete mode 100644 src/arcmira/types/organization_page_response_entity_type.py delete mode 100644 src/arcmira/types/organization_page_response_mentions_by_month_item.py delete mode 100644 src/arcmira/types/organization_page_response_people_item.py delete mode 100644 src/arcmira/types/organization_page_response_people_item_sentiment.py delete mode 100644 src/arcmira/types/organization_page_response_products_item.py delete mode 100644 src/arcmira/types/organization_page_response_products_item_sentiment.py delete mode 100644 src/arcmira/types/organization_page_response_role_edge.py delete mode 100644 src/arcmira/types/organization_page_response_role_edge_label.py delete mode 100644 src/arcmira/types/organization_page_response_role_edge_people_item.py delete mode 100644 src/arcmira/types/organization_page_response_role_edge_recent_appearances_item.py delete mode 100644 src/arcmira/types/organization_page_response_role_edge_role.py delete mode 100644 src/arcmira/types/organization_page_response_stats.py delete mode 100644 src/arcmira/types/organization_page_response_topics_item.py delete mode 100644 src/arcmira/types/organization_page_response_topics_item_sentiment.py delete mode 100644 src/arcmira/types/person_appearance_list_response.py delete mode 100644 src/arcmira/types/person_appearance_list_response_items_item.py delete mode 100644 src/arcmira/types/person_appearance_list_response_items_item_platform.py delete mode 100644 src/arcmira/types/person_appearance_list_response_items_item_sentiment.py delete mode 100644 src/arcmira/types/person_appearance_list_response_items_item_type.py delete mode 100644 src/arcmira/types/person_page_response.py delete mode 100644 src/arcmira/types/person_page_response_appearances_by_month_item.py delete mode 100644 src/arcmira/types/person_page_response_appearances_item.py delete mode 100644 src/arcmira/types/person_page_response_appearances_item_platform.py delete mode 100644 src/arcmira/types/person_page_response_appearances_item_sentiment.py delete mode 100644 src/arcmira/types/person_page_response_appearances_item_type.py delete mode 100644 src/arcmira/types/person_page_response_brands_item.py delete mode 100644 src/arcmira/types/person_page_response_brands_item_sentiment.py delete mode 100644 src/arcmira/types/person_page_response_entity.py delete mode 100644 src/arcmira/types/person_page_response_entity_owned_channels_item.py delete mode 100644 src/arcmira/types/person_page_response_entity_owned_products_item.py delete mode 100644 src/arcmira/types/person_page_response_mentions_by_month_item.py delete mode 100644 src/arcmira/types/person_page_response_mentions_item.py delete mode 100644 src/arcmira/types/person_page_response_mentions_item_platform.py delete mode 100644 src/arcmira/types/person_page_response_mentions_item_sentiment.py delete mode 100644 src/arcmira/types/person_page_response_mentions_item_type.py delete mode 100644 src/arcmira/types/person_page_response_people_item.py delete mode 100644 src/arcmira/types/person_page_response_people_item_role.py delete mode 100644 src/arcmira/types/person_page_response_people_item_sentiment.py delete mode 100644 src/arcmira/types/person_page_response_products_item.py delete mode 100644 src/arcmira/types/person_page_response_products_item_sentiment.py delete mode 100644 src/arcmira/types/person_page_response_role_edge.py delete mode 100644 src/arcmira/types/person_page_response_role_edge_label.py delete mode 100644 src/arcmira/types/person_page_response_role_edge_role.py delete mode 100644 src/arcmira/types/person_page_response_stats.py delete mode 100644 src/arcmira/types/person_page_response_topics_item.py delete mode 100644 src/arcmira/types/person_page_response_topics_item_sentiment.py delete mode 100644 src/arcmira/types/product_page_response.py delete mode 100644 src/arcmira/types/product_page_response_channels_item.py delete mode 100644 src/arcmira/types/product_page_response_channels_item_sentiment.py delete mode 100644 src/arcmira/types/product_page_response_entity.py delete mode 100644 src/arcmira/types/product_page_response_entity_owner.py delete mode 100644 src/arcmira/types/product_page_response_entity_parent_org.py delete mode 100644 src/arcmira/types/product_page_response_entity_type.py delete mode 100644 src/arcmira/types/product_page_response_mentions_by_month_item.py delete mode 100644 src/arcmira/types/product_page_response_opportunities.py delete mode 100644 src/arcmira/types/product_page_response_organizations_item.py delete mode 100644 src/arcmira/types/product_page_response_organizations_item_sentiment.py delete mode 100644 src/arcmira/types/product_page_response_people_item.py delete mode 100644 src/arcmira/types/product_page_response_people_item_sentiment.py delete mode 100644 src/arcmira/types/product_page_response_stats.py delete mode 100644 src/arcmira/types/product_page_response_topics_item.py delete mode 100644 src/arcmira/types/product_page_response_topics_item_sentiment.py rename src/arcmira/types/{channel_page_response_stats.py => publication_window.py} (50%) delete mode 100644 src/arcmira/types/published_excerpt.py delete mode 100644 src/arcmira/types/published_excerpt_public_source_class.py create mode 100644 src/arcmira/types/recommendation_class.py create mode 100644 src/arcmira/types/recommendation_enrichment_item_class.py delete mode 100644 src/arcmira/types/recommendation_list_response_entity.py delete mode 100644 src/arcmira/types/search_resolve_response.py delete mode 100644 src/arcmira/types/search_resolve_response_entity.py rename src/arcmira/types/{channel_page_response_recommendations_summary.py => slack_integration_list_response.py} (54%) create mode 100644 src/arcmira/types/slack_integration_list_response_integrations_item.py rename src/arcmira/types/{feedback_correction_result_recommendation_media_source_channel.py => slack_integration_list_response_integrations_item_channels_item.py} (69%) delete mode 100644 src/arcmira/types/speaker_identification_submitted_response.py delete mode 100644 src/arcmira/types/speaker_identification_submitted_response_identification.py delete mode 100644 src/arcmira/types/speaker_identification_submitted_response_identification_entity.py delete mode 100644 src/arcmira/types/speaker_identification_submitted_response_identification_status.py delete mode 100644 src/arcmira/types/team_member.py delete mode 100644 src/arcmira/types/team_member_role.py delete mode 100644 src/arcmira/types/team_member_seat_type.py delete mode 100644 src/arcmira/types/team_member_spend.py delete mode 100644 src/arcmira/types/team_member_spend_role.py delete mode 100644 src/arcmira/types/team_member_spend_seat_type.py delete mode 100644 src/arcmira/types/team_members_response.py delete mode 100644 src/arcmira/types/team_usage_event.py delete mode 100644 src/arcmira/types/team_usage_events_response.py delete mode 100644 src/arcmira/types/topic_page_response.py delete mode 100644 src/arcmira/types/topic_page_response_channels_item.py delete mode 100644 src/arcmira/types/topic_page_response_channels_item_sentiment.py delete mode 100644 src/arcmira/types/topic_page_response_companies_item.py delete mode 100644 src/arcmira/types/topic_page_response_companies_item_sentiment.py delete mode 100644 src/arcmira/types/topic_page_response_entity.py delete mode 100644 src/arcmira/types/topic_page_response_entity_type.py delete mode 100644 src/arcmira/types/topic_page_response_mentions_by_month_item.py delete mode 100644 src/arcmira/types/topic_page_response_products_item.py delete mode 100644 src/arcmira/types/topic_page_response_products_item_sentiment.py delete mode 100644 src/arcmira/types/topic_page_response_related_topics_item.py delete mode 100644 src/arcmira/types/topic_page_response_related_topics_item_sentiment.py delete mode 100644 src/arcmira/types/topic_page_response_stats.py delete mode 100644 src/arcmira/types/topic_page_response_voices_item.py delete mode 100644 src/arcmira/types/topic_page_response_voices_item_role.py delete mode 100644 src/arcmira/types/topic_page_response_voices_item_sentiment.py delete mode 100644 src/arcmira/types/transcript_edit_submitted_response.py delete mode 100644 src/arcmira/types/transcript_edit_submitted_response_edit.py delete mode 100644 src/arcmira/types/transcript_edit_submitted_response_edit_status.py delete mode 100644 src/arcmira/types/transcript_preparation_required.py delete mode 100644 src/arcmira/types/transcript_preparation_required_action.py delete mode 100644 src/arcmira/types/transcript_preparation_required_action_body.py delete mode 100644 src/arcmira/types/transcript_preparation_required_action_method.py delete mode 100644 src/arcmira/types/transcript_preparation_required_last_attempt.py delete mode 100644 src/arcmira/types/transcript_preparation_required_quality.py delete mode 100644 src/arcmira/types/transcript_preparation_required_quote.py delete mode 100644 src/arcmira/types/transcript_preparation_required_quote_charge.py delete mode 100644 src/arcmira/types/transcript_preparation_required_quote_charge_unit.py delete mode 100644 src/arcmira/types/transcript_request_submit_response.py create mode 100644 src/arcmira/types/transcript_response_access_details.py create mode 100644 src/arcmira/types/transcript_response_access_details_quote.py create mode 100644 src/arcmira/types/transcript_response_access_details_quote_charge.py rename src/arcmira/types/{transcript_preparation_required_quote_charge_from.py => transcript_response_access_details_quote_charge_from.py} (70%) create mode 100644 src/arcmira/types/transcript_response_access_details_quote_charge_unit.py create mode 100644 src/arcmira/types/transcript_search_response_access_details.py create mode 100644 src/arcmira/types/transcript_search_response_access_details_quote.py create mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge.py create mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py create mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py create mode 100644 src/arcmira/types/transcript_search_response_filters_kind_item.py rename src/arcmira/types/{exposure_meta_free_limit.py => transcript_search_response_unlock.py} (63%) delete mode 100644 src/arcmira/types/video_captions_response.py delete mode 100644 src/arcmira/types/video_merge_list_response.py delete mode 100644 src/arcmira/types/video_merge_list_response_merges_item.py delete mode 100644 src/arcmira/types/video_merge_list_response_merges_item_status.py delete mode 100644 src/arcmira/types/video_merge_submitted_response.py delete mode 100644 src/arcmira/types/video_merge_submitted_response_merge.py delete mode 100644 src/arcmira/types/video_merge_submitted_response_merge_status.py delete mode 100644 src/arcmira/types/withdrawn_response.py create mode 100644 src/arcmira/types/wrong_classification_change_class.py delete mode 100644 src/arcmira/types/wrong_classification_change_mention_class.py delete mode 100644 tests/test_prepare.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..0d4a79c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,59 @@ +# Changelog + +## 0.4.0 + +Generated from the v1 document of 2026-10-02. The document dropped from 88 operations to 36. Routes that left it still serve over HTTP, but the SDK no longer has methods for them. + +Added. + +- `client.monitors.entities.add(id, entity_ids=[...], person_match_mode=None)` follows entities by id in a monitor. It reuses the account's tracker for each entity or creates one, then attaches it. Each id gets one result, and an id that cannot be followed comes back with `attached: false` and a reason. +- `client.integrations.slack.list()` lists the account's active Slack workspaces with the `slack_integration_id` and `slack_channel_id` values a monitor needs for Slack delivery. Slack is connected in the dashboard, not through the API. +- `monitors.create` takes `team_id`. `Monitor` carries `access`, `muted` and `team`. +- `feedback.submit` takes `category` and `mcp_call_id`, and `type="experience"` reports how a task went as a whole. +- List bodies for mentions, recommendations, search, counts and channel videos echo the applied date window as `window: { after, before }`. + +Breaking changes from 0.3. + +- Premium is one read. `transcripts.get(video_id, quality="premium")` answers `ready` (200) when the account owns the transcript. Otherwise it buys the whole video within the plan and the account's on-demand budget and answers `pending` (202) with the `job` and a `Retry-After` header. Read again after `Retry-After`. Repeated reads join the same purchase and never buy twice. `TranscriptResult` is now `TranscriptResult_Ready | TranscriptResult_Pending`. The `preparation_required` state and `TranscriptResult_PreparationRequired` are gone. +- `transcripts.prepare_and_wait` is removed, along with `PreparationError`, `PreparationFailedError`, `PreparationTimeoutError` and `PremiumUnavailableError`. Loop on `transcripts.get(..., quality="premium")` until `state == "ready"`. The README has the loop. +- `transcripts.request` and `transcripts.status` are removed, with the `TranscriptRequestSubmitResponse` type. The Premium read buys and reports its own job. `transcripts.list_requests` still lists past purchases. +- A Premium refusal raises from the read itself. `PaymentRequiredError` (402) carries `quota_exceeded` or `spend_limit_exceeded`, and `ForbiddenError` (403) carries `paid_plan_required`. Nothing is charged. +- Error extras moved under `error.details`. `body.quote` is now `body.error.details.quote`, `body.existing_request_id` is `body.error.details.existing_request_id`, and `body.existing_id` (409 `tracker_already_exists`) is `body.error.details.existing_id`. +- Reads take ids. `mentions.list` and `recommendations.list` require `entity_id` (`ent_N`), and `channel_id` takes a YouTube channel id (`UC` plus 22 characters). `entity_name`, `entity_type` and `channel_name` are gone. A name where an id belongs raises `BadRequestError` with code `id_required` and names the parameter. Resolve names first with `entities.resolve`. +- Dates are `after` and `before`, half-open `[after, before)`, on every dated read. `mentions.list` and `recommendations.list` replace `date_from` and `date_to`. `date_to` was an inclusive day, so `date_to="2026-09-01"` becomes `before="2026-09-02"`. `transcripts.search`, `mentions.count` and `channels.videos.list` replace `published_after` and `published_before`. +- `recommendations.list` takes `class_` (`sponsored`, `organic` or `mention`; omit it for all) in place of `mention_class` (`ad_read`, `endorsement`, `mention` or `all`). `Recommendation` and `RecommendationEnrichmentItem` carry `class_` in place of `mention_class`, and so does `WrongClassificationChange` in feedback corrections. +- `transcripts.search` calls `GET /v1/search`. Its `kind` takes `sponsored`, `organic` or `mention` in place of `mention`, `recommendation_sponsored` and `recommendation_organic`. The response renames `requested_k` to `limit` and `returned_n` to `returned`, and `filters.published_after` and `filters.published_before` move to `window`. `TranscriptSearchChunk.text_withheld` is gone. +- List bodies name their collection. `MentionListResponse.data` is `mentions`, `RecommendationListResponse.data` is `recommendations`, and `AlertListResponse.data` is `alerts`. Iterating a pager is unchanged. +- Integer ids and `MM:SS` strings are gone. `Mention` drops `appearance_id`, `start_timestamp` and `end_timestamp`, and `MentionMedia` drops `id`. `Recommendation` drops `recommendation_id`, `start_timestamp` and `end_timestamp`. Use `start_seconds`, `end_seconds` and `media.video_id`. `Alert` replaces `media_id` and `appearance_id` with `video_id`. `Entity.numeric_id` is gone. Premium `speakers[].entity_id` is an `ent_N` string, where 0.3 had an integer. Feedback ids are `fbk_N`. +- `monitors.update` takes `paused` in place of `is_paused`, and `is_collapsed` and `sort_order` are gone. `Monitor`, `Tracker` and monitor tracker rows carry `paused` in place of `is_paused`, and `Monitor` drops `is_collapsed` and `sort_order`. +- Models use the snake_case wire names. Attribute names were already snake_case in 0.3, but the camelCase aliases (`videoId`, `watchUrl`, `invitationStatus` and others) are gone, so `model_dump(by_alias=True)` and the JSON on the wire now match the attribute names. +- `MentionCountsResponse` replaces `published_after` and `published_before` with `window`. `FeedbackCorrectionResult` drops `new_mention_class`, `previous_mention_class`, `recommendation` and `rows_affected`. +- `feedback.submit` no longer requires `query`. `type` stays required. +- `trackers.create` follows a channel by its YouTube channel id in `entity_name`. A channel name raises `id_required`. + +Removed methods and their replacements. + +| 0.3 | 0.4 | +|---|---| +| `entities.search`, `entities.lookup`, `entities.cards` | `entities.resolve`, then `entities.get` | +| `entities.mentions.list(id)` | `mentions.list(entity_id=id)` | +| `entities.recommendations.list(id)` | `recommendations.list(entity_id=id)` | +| `people.get`, `topics.get`, `organizations.get`, `products.get`, `channels.get` | `entities.resolve`, then `entities.get`. For a channel, `channels.coverage` and `channels.videos.list` | +| `people.appearances.list` | `mentions.list(entity_id=..., is_appearance=True)` | +| `channels.related.*` | `mentions.count(channel_ids=..., entity_types=...)` | +| `channels.guests.list` | `mentions.count(channel_ids=..., entity_types="person", mode="appearances")` | +| `people.related.*`, `topics.related.*`, `organizations.related.*`, `products.related.*` | No direct replacement. `mentions.list(entity_id=...)` gives the episodes, and `mentions.count(video_ids=...)` counts what else they mention | +| `transcripts.captions` | `transcripts.get(video_id)`. `languages` lists every caption track and `language` selects one | +| `transcripts.request`, `transcripts.status`, `transcripts.prepare_and_wait` | `transcripts.get(video_id, quality="premium")`, read again on 202 | +| `corrections.*`, `transcripts.edits.*`, `transcripts.speakers.*`, `transcripts.merges.*` | No SDK method. Report a wrong row with `feedback.submit` | +| `team.members`, `team.spend`, `team.usage_events.list` | None. The routes are deleted | + +## 0.3.0 + +The first usable SDK, replacing the URL-only placeholder. The public URL constants remain available. + +- Generated synchronous `Arcmira` and asynchronous `AsyncArcmira` clients with typed responses and cursor pagination. +- `transcripts.get` returns a union discriminated on `state`. The Premium purchase is one job, created by `transcripts.request` and polled with `transcripts.status`. +- `transcripts.prepare_and_wait` reads Premium, posts the purchase once when needed, polls at the pace the API sets and returns the transcript. +- `ApiError` text leads with the status, code and message. +- Licensed under Apache-2.0. diff --git a/README.md b/README.md index d21bca1..e39a165 100644 --- a/README.md +++ b/README.md @@ -6,89 +6,151 @@ The official Python client for the [Arcmira API](https://arcmira.com/docs), with pip install arcmira ``` -Set `ARCMIRA_API_KEY` or pass `api_key` to the client. +Set `ARCMIRA_API_KEY` before you import the client, or pass `api_key` to it. -## Premium quickstart +## First call + +Reads take ids. Resolve a name to an id, then read with the id. ```python from arcmira import Arcmira -transcript = Arcmira().transcripts.prepare_and_wait("dQw4w9WgXcQ") -print(transcript.lines) +client = Arcmira() +match = client.entities.resolve(q="Ramp", type="organization") +ramp = match.best or match.suggested +if ramp is None: + raise LookupError(match.note) +for mention in client.mentions.list(entity_id=ramp.id, after="2026-09-01", before="2026-10-01"): + print(mention.media.video_id, mention.start_seconds, mention.description) ``` -`prepare_and_wait(video_id, *, max_on_demand_cents=0, timeout_seconds=300)` reads the Premium transcript and returns it. When your account does not own it yet, the call posts `{video_id}` to `/v1/transcriptions` once, polls the job at the pace the API sets with `Retry-After` and `next_poll_seconds`, and reads the finished transcript. The default spends included credits only and sends no `Idempotency-Key`. Preparation purchases the whole video, even if you only read a window. +`entities.resolve` answers one of three ways. `best` is a certain match. `suggested` is the likeliest row, with a reason, when no row is certain. `ask` lists the options when several rows fit, and `best` and `suggested` are both `None`. For a show, pass `type="channel"` and use `best.youtube_channel_id` as `channel_id`. + +A name where an id belongs raises `BadRequestError` with the code `id_required`. Its message names the parameter and the resolve call. + +## Dates -`AsyncArcmira` has the same method: `await client.transcripts.prepare_and_wait(video_id)`. +Every dated read takes `after` and `before`. The window is half-open, `[after, before)`. Each accepts an ISO date (`2026-09-01`) or a datetime with an offset (`2026-09-01T00:00:00Z`), read in UTC. Each dated read echoes the window it applied in `window`. -Errors you can handle: +## Premium transcripts -- `PreparationTimeoutError` when the job outlasts `timeout_seconds`. It carries `.job`, and the job keeps running. -- `PreparationFailedError` when the job fails or is refunded. It carries `.job`. -- `PremiumUnavailableError` when the plan has no Premium. The read returned captions, and they are never returned as Premium. -- `ApiError` for any API refusal, such as `quota_exceeded`, with the public error and recovery URLs in `body`. +A Premium read is one call. It answers 200 `ready` when the account owns the transcript. Otherwise it buys the whole video within the account's plan and answers 202 `pending` with the job. Included credits are spent first, then the account's on-demand budget. The budget is the approval, so the call takes no price ceiling. -To spend money, pass a cents ceiling you have approved. The call then sends the quoted `max_rows` and a generated `Idempotency-Key`. +Read again after `Retry-After`. Repeated reads join the same purchase and never buy twice. ```python +import time +from arcmira import Arcmira + client = Arcmira() -transcript = client.transcripts.prepare_and_wait("dQw4w9WgXcQ", max_on_demand_cents=25) +for _ in range(60): + read = client.transcripts.with_raw_response.get("dQw4w9WgXcQ", quality="premium") + if read.data.state == "ready": + for line in read.data.lines: + print(line.start, line.text) + break + time.sleep(int(read.headers.get("retry-after") or read.data.job.next_poll_seconds or 10)) ``` -## Lower-level calls +`read.data` is a `TranscriptResult`, discriminated on `state`. `ready` carries the transcript. `pending` carries `job`, with `eta_seconds`, `next_poll_seconds` and `charge`. Without `with_raw_response`, `client.transcripts.get(...)` returns the same union without the status and headers. -A quote is free. GET never purchases Premium, and each state is typed. +A quote is free and changes nothing. ```python -client = Arcmira() -quote = client.transcripts.quote(video_id="dQw4w9WgXcQ") -print(quote.quote.rows, quote.charge) - -result = client.transcripts.with_raw_response.get(video_id="dQw4w9WgXcQ", quality="premium") -if result.data.state == "ready": - print(result.data.lines) -elif result.data.state == "preparation_required": - print(result.data.quote, result.data.action) -else: - print(result.data.job.status_url, result.data.job.next_poll_seconds) -print(result.status_code, result.headers) +quote = client.transcripts.quote("dQw4w9WgXcQ") +print(quote.quote.rows, quote.charge.amount, quote.charge.from_, quote.max_on_demand_cents) ``` -To prepare without waiting, post the purchase and poll the job yourself. Every purchase answers one `Job`. `idempotency_key` is optional while `max_on_demand_cents` is 0: without one, a purchase already open or owned for the video is returned with `existing: true`. `max_on_demand_cents` defaults to zero. A positive amount requires both `idempotency_key` and `max_rows`, and a retry with the same saved key and exact input returns the same job with the `Idempotency-Replayed` header. +`client.transcripts.list_requests()` lists past purchases with their state. + +A read without `quality="premium"` returns captions and buys nothing. + +## Errors + +A refusal raises a typed error from `arcmira.errors`. Each derives from `arcmira.core.api_error.ApiError` and carries `status_code`, `headers` and `body`. `body.error` holds `type`, `code`, `message`, `param`, `gate`, `unlock`, `details`, `doc_url` and `request_id`. Switch on `type` and `gate` first. Codes inside a type can grow. + +| Status | Error | Example codes | +|---|---|---| +| 400 | `BadRequestError` | `invalid_query`, `id_required`, `invalid_cursor` | +| 401 | `UnauthorizedError` | `invalid_api_key` | +| 402 | `PaymentRequiredError` | `quota_exceeded`, `spend_limit_exceeded` | +| 403 | `ForbiddenError` | `paid_plan_required`, `freshness_requires_paid` | +| 404 | `NotFoundError` | `entity_not_found` | +| 409 | `ConflictError` | `tracker_already_exists` | +| 429 | `TooManyRequestsError` | `rate_limited` | +| 500, 503 | `InternalServerError`, `ServiceUnavailableError` | | + +A priced refusal carries the price in `error.details.quote`. Nothing is charged. ```python -order = client.transcripts.with_raw_response.request(video_id="dQw4w9WgXcQ") -print(order.status_code, order.data.job.state) -job = client.transcripts.status(order.data.job.id) +from arcmira.errors import ForbiddenError, PaymentRequiredError + +try: + client.transcripts.get("dQw4w9WgXcQ", quality="premium") +except (PaymentRequiredError, ForbiddenError) as refusal: + error = refusal.body.error + print(error.code, error.details.quote.rows, error.unlock.url) ``` -A refusal raises a typed error derived from `arcmira.core.api_error.ApiError`. Its `body` retains the public error, quote, and recovery URLs. `refund_pending` is unfinished; a refund is complete only when the job reports `refunded`. +`str(refusal)` reads `402 quota_exceeded: `. A duplicate tracker carries the existing id in `error.details.existing_id`. + +## Pagination + +`mentions.list`, `recommendations.list`, `channels.videos.list` and `transcripts.list_requests` return pagers. Iterate them and they follow `next_cursor` for you. Cursors are opaque. Keep the filters the same between pages. ```python -for job in client.transcripts.list_requests(limit=10): - print(job.id, job.state) -for episode in client.channels.videos.list(channel_id="UC-DRzaGnL_vtBUpCFH5M0tg", limit=10): +for episode in client.channels.videos.list("UC-DRzaGnL_vtBUpCFH5M0tg", limit=10): print(episode.video_id) ``` -Pagination follows the actual `requests` and `episodes` arrays. Cursors remain opaque and filters stay the same between pages. +Each page body names its rows: `mentions`, `recommendations`, `episodes` or `requests`. The alert lists (`monitors.alerts.list`, `trackers.alerts.list`) return one page of `alerts`, newest first. Pass a larger `limit` to read further. + +## Async -For asynchronous calls, use `AsyncArcmira` and await the same methods. Paginated methods return async iterators after awaiting the initial page. +`AsyncArcmira` has the same methods to await. Paginated methods return async iterators after you await the first page. ```python from arcmira import AsyncArcmira -async def history(): +async def ramp_sponsorships(): client = AsyncArcmira() - async for request in await client.transcripts.list_requests(limit=10): - print(request.id) + async for row in await client.recommendations.list(entity_id="ent_14", class_="sponsored"): + print(row.class_, row.media.video_id, row.start_seconds) ``` -See the [generated reference](reference.md) for all endpoints. +`class` is a Python keyword, so the parameter and the field are spelled `class_`. The wire name stays `class`. + +## Methods + +Each method has the full parameter list in the [generated reference](reference.md). + +| Group | Methods | +|---|---| +| `entities` | `resolve`, `get`, `momentum` | +| `mentions` | `list`, `count` | +| `recommendations` | `list` | +| `transcripts` | `search`, `get`, `quote`, `list_requests` | +| `channels` | `coverage`, `videos.list`, `sponsors.list` | +| `monitors` | `list`, `create`, `update`, `delete`, `rotate_webhook_secret`, `trackers.list`, `trackers.add`, `entities.add`, `alerts.list` | +| `trackers` | `list`, `create`, `update`, `delete`, `alerts.list` | +| `integrations` | `slack.list` | +| `feedback` | `submit`, `get` | +| `me` | `get`, `update_settings` | +| `health` | `check` | + +`transcripts.search` returns spoken passages from `GET /v1/search`. Its filters take ids too. + +To follow an entity you have an id for, call `monitors.entities.add(monitor_id, entity_ids=["ent_14"])`. To watch an exact name before it is indexed, call `trackers.create(entity_name="Ramp", entity_type="organization")`. A channel tracker takes the YouTube channel id as `entity_name`. + +See [CHANGELOG.md](CHANGELOG.md) for what changed from 0.3. + +## Agents + +The [Arcmira MCP server](https://github.com/arcmira/mcp) gives AI agents the same data at `https://mcp.arcmira.com/mcp`. [llms.txt](llms.txt) describes this package for agents. ## Regenerate and verify -Run `bash scripts/generate.sh` with Node 22 or newer, Python 3, Docker, and Fern access for the `arcmira` organization. Generation pins Fern CLI 5.131.1 and Python generator 5.31.0, disables CLI version redirection and telemetry, and reads `fern/openapi.json`. The overlay combines distinct success schemas and rejects unknown or ambiguous cursor collections. Generated source is never edited by hand. `scripts/overrides/` holds the hand-written pieces the installer copies back after every generation: the `ApiError` text and `prepare_and_wait`. +Run `bash scripts/generate.sh` with Node 22 or newer, Python 3, Docker, and Fern access for the `arcmira` organization. Generation pins Fern CLI 5.131.1 and Python generator 5.31.0, disables CLI version redirection and telemetry, and reads `fern/openapi.json`. `fern/method-names.json` names the group and method of every operation by operationId. An operation without a name, or a name for an operation the document lacks, fails the build. The overlay combines the transcript read's success schemas into `TranscriptResult` and rejects unknown or ambiguous cursor collections. Generated source is never edited by hand. `scripts/overrides/api_error.py` holds the `ApiError` text, and the installer copies it back after every generation. ```sh uv sync @@ -96,9 +158,7 @@ uv run python -m unittest discover -s tests -v uv build ``` -The tests use a local HTTP server that returns the bodies the API sends. They check both client variants, state discrimination, `prepare_and_wait`, response status, quotes, refusals, replay input, and opaque pagination. No live API key or purchase is required. - -Version 0.3.0 replaces the earlier URL-only placeholder with a usable SDK. The public URL constants remain available. +The tests use a local HTTP server that returns the bodies the API sends. They check both client variants, the ready and pending reads, typed refusals with their quote, the query and body each call sends, and opaque pagination. No live API key or purchase is required. ## License diff --git a/VERSION b/VERSION index 0d91a54..1d0ba9e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.0 +0.4.0 diff --git a/fern/method-names.json b/fern/method-names.json index e791db7..35ad9d9 100644 --- a/fern/method-names.json +++ b/fern/method-names.json @@ -1,549 +1,208 @@ { - "get /v1/channels/{}/coverage": { + "get_health": { "group": [ - "channels" + "health" ], - "method": "coverage" + "method": "check" }, - "get /v1/channels/{}": { + "get_me": { "group": [ - "channels" + "me" ], "method": "get" }, - "get /v1/channels/{}/guests": { - "group": [ - "channels", - "guests" - ], - "method": "list" - }, - "get /v1/channels/{}/topics": { - "group": [ - "channels", - "related" - ], - "method": "topics" - }, - "get /v1/channels/{}/people": { - "group": [ - "channels", - "related" - ], - "method": "people" - }, - "get /v1/channels/{}/organizations": { + "update_my_settings": { "group": [ - "channels", - "related" + "me" ], - "method": "organizations" + "method": "updateSettings" }, - "get /v1/channels/{}/products": { + "resolve_entity": { "group": [ - "channels", - "related" + "entities" ], - "method": "products" + "method": "resolve" }, - "get /v1/channels/{}/channels": { + "get_entity": { "group": [ - "channels", - "related" + "entities" ], - "method": "channels" + "method": "get" }, - "get /v1/channels/{}/sponsors": { + "get_entity_momentum": { "group": [ - "channels", - "sponsors" + "entities" ], - "method": "list" + "method": "momentum" }, - "get /v1/channels/{}/videos": { + "list_mentions": { "group": [ - "channels", - "videos" + "mentions" ], "method": "list" }, - "post /v1/videos/{}/corrections": { - "group": [ - "corrections" - ], - "method": "submit" - }, - "delete /v1/corrections/speaker-edits/{}": { + "count_mentions": { "group": [ - "corrections" - ], - "method": "withdrawSpeakerEdit" - }, - "delete /v1/corrections/entity-tags/{}": { - "group": [ - "corrections" + "mentions" ], - "method": "withdrawEntityTag" + "method": "count" }, - "delete /v1/corrections/segment-rewrites/{}": { + "list_recommendations": { "group": [ - "corrections" + "recommendations" ], - "method": "withdrawSegmentRewrite" + "method": "list" }, - "get /v1/entities/search": { + "search": { "group": [ - "entities" + "transcripts" ], "method": "search" }, - "get /v1/entities/resolve": { - "group": [ - "entities" - ], - "method": "resolve" - }, - "get /v1/entities/lookup": { + "get_transcript": { "group": [ - "entities" + "transcripts" ], - "method": "lookup" + "method": "get" }, - "get /v1/entities/cards": { + "quote_transcription": { "group": [ - "entities" + "transcripts" ], - "method": "cards" + "method": "quote" }, - "get /v1/entities/{}": { + "list_transcriptions": { "group": [ - "entities" + "transcripts" ], - "method": "get" + "method": "listRequests" }, - "get /v1/entities/{}/momentum": { + "get_channel_coverage": { "group": [ - "entities" + "channels" ], - "method": "momentum" + "method": "coverage" }, - "get /v1/entities/{}/mentions": { + "list_channel_videos": { "group": [ - "entities", - "mentions" + "channels", + "videos" ], "method": "list" }, - "get /v1/entities/{}/recommendations": { + "list_channel_sponsors": { "group": [ - "entities", - "recommendations" + "channels", + "sponsors" ], "method": "list" }, - "post /v1/feedback": { + "submit_feedback": { "group": [ "feedback" ], "method": "submit" }, - "get /v1/feedback/{}": { + "get_feedback": { "group": [ "feedback" ], "method": "get" }, - "get /v1/health": { - "group": [ - "health" - ], - "method": "check" - }, - "get /v1/me": { - "group": [ - "me" - ], - "method": "get" - }, - "patch /v1/me/settings": { - "group": [ - "me" - ], - "method": "updateSettings" - }, - "get /v1/mentions": { - "group": [ - "mentions" - ], - "method": "list" - }, - "get /v1/mentions/counts": { - "group": [ - "mentions" - ], - "method": "count" - }, - "get /v1/monitors": { + "list_monitors": { "group": [ "monitors" ], "method": "list" }, - "post /v1/monitors": { + "create_monitor": { "group": [ "monitors" ], "method": "create" }, - "delete /v1/monitors/{}": { + "update_monitor": { "group": [ "monitors" ], - "method": "delete" + "method": "update" }, - "patch /v1/monitors/{}": { + "delete_monitor": { "group": [ "monitors" ], - "method": "update" + "method": "delete" }, - "post /v1/monitors/{}/webhook-secret/rotate": { + "rotate_monitor_webhook_secret": { "group": [ "monitors" ], "method": "rotateWebhookSecret" }, - "get /v1/monitors/{}/alerts": { - "group": [ - "monitors", - "alerts" - ], - "method": "list" - }, - "get /v1/monitors/{}/trackers": { + "list_monitor_trackers": { "group": [ "monitors", "trackers" ], "method": "list" }, - "post /v1/monitors/{}/trackers": { + "add_monitor_trackers": { "group": [ "monitors", "trackers" ], "method": "add" }, - "get /v1/organizations/{}": { - "group": [ - "organizations" - ], - "method": "get" - }, - "get /v1/organizations/{}/topics": { - "group": [ - "organizations", - "related" - ], - "method": "topics" - }, - "get /v1/organizations/{}/people": { - "group": [ - "organizations", - "related" - ], - "method": "people" - }, - "get /v1/organizations/{}/organizations": { - "group": [ - "organizations", - "related" - ], - "method": "organizations" - }, - "get /v1/organizations/{}/products": { - "group": [ - "organizations", - "related" - ], - "method": "products" - }, - "get /v1/organizations/{}/channels": { - "group": [ - "organizations", - "related" - ], - "method": "channels" - }, - "get /v1/people/{}": { - "group": [ - "people" - ], - "method": "get" - }, - "get /v1/people/{}/appearances": { - "group": [ - "people", - "appearances" - ], - "method": "list" - }, - "get /v1/people/{}/topics": { - "group": [ - "people", - "related" - ], - "method": "topics" - }, - "get /v1/people/{}/people": { - "group": [ - "people", - "related" - ], - "method": "people" - }, - "get /v1/people/{}/organizations": { - "group": [ - "people", - "related" - ], - "method": "organizations" - }, - "get /v1/people/{}/products": { - "group": [ - "people", - "related" - ], - "method": "products" - }, - "get /v1/people/{}/channels": { - "group": [ - "people", - "related" - ], - "method": "channels" - }, - "get /v1/products/{}": { + "add_monitor_entities": { "group": [ - "products" - ], - "method": "get" - }, - "get /v1/products/{}/topics": { - "group": [ - "products", - "related" - ], - "method": "topics" - }, - "get /v1/products/{}/people": { - "group": [ - "products", - "related" - ], - "method": "people" - }, - "get /v1/products/{}/organizations": { - "group": [ - "products", - "related" - ], - "method": "organizations" - }, - "get /v1/products/{}/products": { - "group": [ - "products", - "related" - ], - "method": "products" - }, - "get /v1/products/{}/channels": { - "group": [ - "products", - "related" + "monitors", + "entities" ], - "method": "channels" + "method": "add" }, - "get /v1/recommendations": { + "list_monitor_alerts": { "group": [ - "recommendations" + "monitors", + "alerts" ], "method": "list" }, - "get /v1/team/members": { + "list_slack_integrations": { "group": [ - "team" - ], - "method": "members" - }, - "get /v1/team/spend": { - "group": [ - "team" - ], - "method": "spend" - }, - "get /v1/team/usage-events": { - "group": [ - "team", - "usageEvents" + "integrations", + "slack" ], "method": "list" }, - "get /v1/topics/{}": { - "group": [ - "topics" - ], - "method": "get" - }, - "get /v1/topics/{}/topics": { - "group": [ - "topics", - "related" - ], - "method": "topics" - }, - "get /v1/topics/{}/people": { - "group": [ - "topics", - "related" - ], - "method": "people" - }, - "get /v1/topics/{}/organizations": { - "group": [ - "topics", - "related" - ], - "method": "organizations" - }, - "get /v1/topics/{}/products": { - "group": [ - "topics", - "related" - ], - "method": "products" - }, - "get /v1/topics/{}/channels": { - "group": [ - "topics", - "related" - ], - "method": "channels" - }, - "get /v1/trackers": { + "list_trackers": { "group": [ "trackers" ], "method": "list" }, - "post /v1/trackers": { + "create_tracker": { "group": [ "trackers" ], "method": "create" }, - "delete /v1/trackers/{}": { + "update_tracker": { "group": [ "trackers" ], - "method": "delete" + "method": "update" }, - "patch /v1/trackers/{}": { + "delete_tracker": { "group": [ "trackers" ], - "method": "update" + "method": "delete" }, - "get /v1/trackers/{}/alerts": { + "list_tracker_alerts": { "group": [ "trackers", "alerts" ], "method": "list" - }, - "get /v1/transcripts/search": { - "group": [ - "transcripts" - ], - "method": "search" - }, - "get /v1/transcripts/{}": { - "group": [ - "transcripts" - ], - "method": "get" - }, - "get /v1/videos/{}/captions": { - "group": [ - "transcripts" - ], - "method": "captions" - }, - "get /v1/transcriptions": { - "group": [ - "transcripts" - ], - "method": "listRequests" - }, - "post /v1/transcriptions": { - "group": [ - "transcripts" - ], - "method": "request" - }, - "get /v1/transcriptions/{}": { - "group": [ - "transcripts" - ], - "method": "status" - }, - "post /v1/transcripts/{}/edits": { - "group": [ - "transcripts", - "edits" - ], - "method": "submit" - }, - "delete /v1/transcripts/{}/edits/{}": { - "group": [ - "transcripts", - "edits" - ], - "method": "withdraw" - }, - "get /v1/transcripts/{}/merges": { - "group": [ - "transcripts", - "merges" - ], - "method": "list" - }, - "post /v1/transcripts/{}/merges": { - "group": [ - "transcripts", - "merges" - ], - "method": "submit" - }, - "delete /v1/transcripts/{}/merges/{}": { - "group": [ - "transcripts", - "merges" - ], - "method": "withdraw" - }, - "post /v1/transcripts/{}/speakers": { - "group": [ - "transcripts", - "speakers" - ], - "method": "identify" - }, - "delete /v1/transcripts/{}/speakers/{}": { - "group": [ - "transcripts", - "speakers" - ], - "method": "withdraw" } } diff --git a/fern/openapi.json b/fern/openapi.json index 4e09c1c..f724757 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -215,61 +215,6 @@ "Error": { "type": "object", "properties": { - "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." - }, - "existingId": { - "type": "string", - "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." - }, "error": { "type": "object", "properties": { @@ -382,6 +327,67 @@ "type": "integer", "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." }, + "details": { + "type": "object", + "properties": { + "quote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." + }, + "existing_request_id": { + "type": "string", + "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + }, + "existing_id": { + "type": "string", + "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." + } + }, + "description": "Machine data the refusal carries for you to act on. Present only on the codes that name a field here." + }, "doc_url": { "type": "string" }, @@ -584,6 +590,16 @@ "type": "not_found", "description": "No route or resource at this path." }, + { + "code": "owner_only", + "type": "permission_error", + "description": "Only the team owner may change or test a team monitor's webhook, rotate its secret, or delete it." + }, + { + "code": "owner_plan_required", + "type": "permission_error", + "description": "A team monitor follows the team owner's plan, which does not include this. The owner can upgrade; a member's own plan does not apply." + }, { "code": "pagination_gated", "type": "permission_error", @@ -604,7 +620,7 @@ { "code": "purchase_authority_changed", "type": "conflict_error", - "description": "The account billing authority (plan, team, or on-demand controls) changed after the intent was accepted. Nothing was charged. Review the quote and send a new intent." + "description": "The account billing authority (plan or on-demand controls) changed after the intent was accepted. Nothing was charged. Review the quote and send a new intent." }, { "code": "quota_exceeded", @@ -679,20 +695,15 @@ "type": "quota_exceeded", "description": "The purchase would take on-demand spending past the account or seat spend limit this period. Nothing was charged. Raise the limit or wait for the next period, then send a new intent." }, - { - "code": "team_key_required", - "type": "permission_error", - "description": "The route needs a team-scoped key; personal keys are refused." - }, { "code": "team_not_found", "type": "not_found", - "description": "The key names a team that no longer exists." + "description": "No team with this id that the caller belongs to." }, { "code": "tracker_already_exists", "type": "conflict_error", - "description": "The account already tracks this entity. existingId identifies the existing tracker." + "description": "The account already tracks this entity. error.details.existing_id identifies the existing tracker." }, { "code": "tracker_not_found", @@ -723,25 +734,8 @@ { "code": "webhook_not_configured", "type": "conflict_error", - "description": "The monitor has no webhook to rotate a secret for. Set webhookUrl first." - } - ] - }, - "TranscriptQuote": { - "type": "object", - "properties": { - "quarters": { - "type": "integer", - "description": "Number of 15-minute blocks in the video, ceiling'd, minimum 1." - }, - "rows": { - "type": "integer", - "description": "Total unlock cost in rows: 75 rows per 15-minute block." + "description": "The monitor has no webhook to rotate a secret for. Set webhook_url first." } - }, - "required": [ - "quarters", - "rows" ] }, "ErrorResource": { @@ -1021,6 +1015,23 @@ ], "description": "The value the boundary withheld, not the reason it refused. kind is a closed vocabulary and the fields beside it are fixed per kind; see https://arcmira.com/docs/errors#resource-kinds." }, + "TranscriptQuote": { + "type": "object", + "properties": { + "quarters": { + "type": "integer", + "description": "Number of 15-minute blocks in the video, ceiling'd, minimum 1." + }, + "rows": { + "type": "integer", + "description": "Total unlock cost in rows: 75 rows per 15-minute block." + } + }, + "required": [ + "quarters", + "rows" + ] + }, "HealthResponse": { "type": "object", "properties": { @@ -1184,7 +1195,7 @@ }, "tier": { "type": "string", - "description": "Plan tier, e.g. free, hobby, pro, teams, enterprise." + "description": "Plan tier, e.g. free, hobby, pro, enterprise." }, "scopes": { "type": "array", @@ -1195,7 +1206,7 @@ }, "rate_limit": { "type": "integer", - "description": "Requests allowed per 60-second window for this key: 600 for enterprise/teams, 240 for other paid tiers, 60 for free, unless a per-key override is set." + "description": "Requests allowed per 60-second window for this key: 600 for enterprise, 240 for other paid tiers, 60 for free, unless a per-key override is set." }, "recommendations_api_enabled": { "type": "boolean", @@ -1485,165 +1496,6 @@ "docs_url" ] }, - "SearchResolveResponse": { - "type": "object", - "properties": { - "found": { - "type": "boolean", - "description": "True when the query resolved to exactly one entity." - }, - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id. Use \"ent_{id}\" with the /v1/entities endpoints." - }, - "name": { - "type": "string", - "description": "Canonical entity name." - }, - "type": { - "type": "string", - "description": "Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified)." - } - }, - "required": [ - "id", - "name", - "type" - ], - "description": "The resolved entity. Absent when found is false." - }, - "route": { - "type": "string", - "description": "Site-relative route for the entity on arcmira.com. Absent when found is false." - }, - "fromCache": { - "type": "boolean", - "description": "True when the result was served from the 5-minute resolver cache." - } - }, - "required": [ - "found" - ] - }, - "EntitySearchResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntitySearchResult" - } - }, - "query": { - "type": "string", - "description": "The q parameter echoed back." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows matched than limit. There is no cursor; narrow q or pass type." - } - }, - "required": [ - "data", - "query", - "has_more" - ] - }, - "EntitySearchResult": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Public entity id in the form \"ent_{n}\"." - }, - "numeric_id": { - "type": "integer", - "description": "Raw integer database id." - }, - "name": { - "type": "string", - "description": "Entity name." - }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "URL slug. Null when the entity has never been slugged." - }, - "type": { - "type": "string", - "description": "Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified)." - }, - "appearance_count": { - "type": "integer", - "default": 0, - "description": "Number of indexed appearance/mention rows. Results are ordered by this, descending." - }, - "youtube_channel_id": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id for channel entities. Null for every other type." - }, - "description": { - "type": [ - "string", - "null" - ], - "description": "One catalog sentence that tells rows with the same name apart, for example \"Common gender-neutral given name or nickname\". Null when the catalog has none." - }, - "page": { - "type": [ - "string", - "null" - ], - "description": "The entity's page on arcmira.com, absolute. Link the name to it when you write the entity into an answer. Null for a type the site has no page for." - }, - "suggested": { - "type": "boolean", - "description": "True on an exact match with far more traction than any other row of its type, or on the one row a UC id, @handle, or YouTube URL resolves to. Use it without asking." - }, - "recommendations_summary": { - "type": "object", - "properties": { - "total_ad_reads": { - "type": "integer", - "description": "Total ad_read rows for this entity. 0 when none." - }, - "total_endorsements": { - "type": "integer", - "description": "Total endorsement rows for this entity. 0 when none." - }, - "unique_channels": { - "type": "integer", - "description": "Number of distinct channels with commercial mentions of this entity." - } - }, - "required": [ - "total_ad_reads", - "total_endorsements", - "unique_channels" - ], - "description": "Only present when the caller has Recommendations API access (a Pro+ plan) and a brand profile exists for the entity." - } - }, - "required": [ - "id", - "numeric_id", - "name", - "slug", - "type", - "youtube_channel_id", - "description", - "page", - "suggested" - ] - }, "EntityResolveResponse": { "type": "object", "properties": { @@ -1894,11 +1746,14 @@ ], "description": "Set when best is null but one row stands out, with the reason and evidence. Use it and tell the user you assumed it." }, - "EntityLookupResponse": { + "EntityDetailResponse": { "type": "object", "properties": { "entity": { "$ref": "#/components/schemas/Entity" + }, + "recommendations_summary": { + "$ref": "#/components/schemas/EntityDetailRecommendationsSummary" } }, "required": [ @@ -1912,10 +1767,6 @@ "type": "string", "description": "Public entity id in the form \"ent_{n}\". Always the canonical entity id." }, - "numeric_id": { - "type": "integer", - "description": "Raw integer database id of the canonical entity. Prefer the public \"ent_{n}\" id in requests." - }, "canonical_id": { "type": "string", "description": "Public id of the canonical entity. Identical to id." @@ -2017,7 +1868,6 @@ }, "required": [ "id", - "numeric_id", "canonical_id", "name", "type", @@ -2035,147 +1885,29 @@ "mentions_page" ] }, - "EntityCardsResponse": { - "type": "object", - "properties": { - "cards": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityCard" - }, - "description": "One card per requested id that resolved to an entity, in requested order. Unknown ids are silently dropped." - } - }, - "required": [ - "cards" - ] - }, - "EntityCard": { + "EntityDetailRecommendationsSummary": { "type": "object", "properties": { - "id": { + "total_ad_reads": { "type": "integer", - "description": "The REQUESTED raw integer entity id (even when it was merged; the card carries the canonical entity's data under the requested id)." + "description": "Total ad_read rows across all channels. 0 when none." }, - "name": { - "type": "string", - "description": "Canonical entity name." + "total_endorsements": { + "type": "integer", + "description": "Total endorsement rows across all channels. 0 when none." }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "URL slug. Null when the entity has never been slugged." + "unique_shows": { + "type": "integer", + "description": "Number of distinct shows/channels with commercial mentions of this entity." }, - "type": { + "first_seen_at": { "type": [ "string", "null" ], - "description": "Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified)." + "description": "Timestamp of the earliest commercial mention. Null until the brand profile has been computed." }, - "image_url": { - "type": [ - "string", - "null" - ], - "description": "Entity image URL. Null until an image has been resolved." - }, - "subtitle": { - "type": "null", - "description": "Reserved for a future category/role line. Always null today." - }, - "index_mentions": { - "type": "integer", - "description": "Total indexed mention rows for the entity. 0 when none." - }, - "index_appearances": { - "type": [ - "integer", - "null" - ], - "description": "Indexed physical-appearance rows. Only populated for person entities; null for every other type." - }, - "indexed_videos": { - "type": [ - "integer", - "null" - ], - "minimum": 0, - "description": "Completed videos published by this channel and indexed by Arcmira. Null for other entity types." - }, - "average_views": { - "type": [ - "number", - "null" - ], - "minimum": 0, - "description": "Mean of known positive view counts for the channel's indexed videos. Null when unavailable or for other types." - }, - "youtube_channel_id": { - "type": [ - "string", - "null" - ], - "description": "Canonical YouTube channel identifier, when known." - }, - "youtube_handle": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel handle, when known." - } - }, - "required": [ - "id", - "name", - "slug", - "type", - "image_url", - "subtitle", - "index_mentions", - "index_appearances" - ] - }, - "EntityDetailResponse": { - "type": "object", - "properties": { - "entity": { - "$ref": "#/components/schemas/Entity" - }, - "recommendations_summary": { - "$ref": "#/components/schemas/EntityDetailRecommendationsSummary" - } - }, - "required": [ - "entity" - ] - }, - "EntityDetailRecommendationsSummary": { - "type": "object", - "properties": { - "total_ad_reads": { - "type": "integer", - "description": "Total ad_read rows across all channels. 0 when none." - }, - "total_endorsements": { - "type": "integer", - "description": "Total endorsement rows across all channels. 0 when none." - }, - "unique_shows": { - "type": "integer", - "description": "Number of distinct shows/channels with commercial mentions of this entity." - }, - "first_seen_at": { - "type": [ - "string", - "null" - ], - "description": "Timestamp of the earliest commercial mention. Null until the brand profile has been computed." - }, - "last_seen_at": { + "last_seen_at": { "type": [ "string", "null" @@ -2200,11 +1932,12 @@ "MentionListResponse": { "type": "object", "properties": { - "data": { + "mentions": { "type": "array", "items": { "$ref": "#/components/schemas/Mention" - } + }, + "description": "Newest first." }, "has_more": { "type": "boolean", @@ -2227,6 +1960,9 @@ } ] }, + "window": { + "$ref": "#/components/schemas/PublicationWindow" + }, "note": { "type": "string", "description": "Present on a free preview page: where the list stops and the plan that lifts it. Say so rather than calling this every mention." @@ -2251,10 +1987,11 @@ } }, "required": [ - "data", + "mentions", "has_more", "next_cursor", - "entity" + "entity", + "window" ] }, "Mention": { @@ -2264,20 +2001,12 @@ "type": "string", "description": "Public mention id in the form \"men_{n}\"." }, - "appearance_id": { - "type": "integer", - "description": "Raw integer id of the underlying appearance row. Same number as in the \"men_{n}\" public id." - }, "entity": { "$ref": "#/components/schemas/EntityRef" }, "media": { "type": "object", "properties": { - "id": { - "type": "integer", - "description": "Raw integer media row id." - }, "video_id": { "type": "string", "description": "YouTube video id (11 characters)." @@ -2351,7 +2080,6 @@ } }, "required": [ - "id", "video_id", "title", "url", @@ -2361,35 +2089,19 @@ "source_channel" ] }, - "start_timestamp": { - "type": [ - "string", - "null" - ], - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds. Null when the analyzer could not locate the mention in time." - }, - "end_timestamp": { - "type": [ - "string", - "null" - ], - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds. Null when unknown." - }, "start_seconds": { "type": [ "integer", "null" ], - "description": "Start position in the video in integer SECONDS, parsed from start_timestamp. Prefer this over the deprecated string field. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "Start position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." }, "end_seconds": { "type": [ "integer", "null" ], - "description": "End position in the video in integer SECONDS, parsed from end_timestamp. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "End position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." }, "is_appearance": { "type": "boolean", @@ -2454,7 +2166,7 @@ "items": { "$ref": "#/components/schemas/RecommendationEnrichmentItem" }, - "description": "Commercial mentions (ad reads, endorsements) for the same entity in the same video." + "description": "Commercial mentions (sponsored and organic) for the same entity in the same video." } }, "required": [ @@ -2465,11 +2177,8 @@ }, "required": [ "id", - "appearance_id", "entity", "media", - "start_timestamp", - "end_timestamp", "start_seconds", "end_seconds", "is_appearance", @@ -2527,9 +2236,14 @@ "type": "string", "description": "Public recommendation id in the form \"com_{n}\"." }, - "mention_class": { + "class": { "type": "string", - "description": "Commercial mention classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention)." + "enum": [ + "sponsored", + "organic", + "mention" + ], + "description": "Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention)." }, "verbatim_quote": { "type": [ @@ -2556,52 +2270,65 @@ "type": "number", "description": "Classifier confidence between 0 and 1." }, - "start_timestamp": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds." - }, - "end_timestamp": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds." - }, "start_seconds": { "type": [ "integer", "null" ], - "description": "Start position in the video in integer SECONDS, parsed from start_timestamp. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "Start position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." }, "end_seconds": { "type": [ "integer", "null" ], - "description": "End position in the video in integer SECONDS, parsed from end_timestamp. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "End position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." } }, "required": [ "id", - "mention_class", + "class", "verbatim_quote", "promo_code", "offer", "confidence", - "start_timestamp", - "end_timestamp", "start_seconds", "end_seconds" ] }, + "PublicationWindow": { + "type": "object", + "properties": { + "after": { + "type": [ + "string", + "null" + ], + "description": "Inclusive start as an ISO instant. Null when the window has no start." + }, + "before": { + "type": [ + "string", + "null" + ], + "description": "Exclusive end as an ISO instant. Null when the window has no end. Earlier than the before you sent when your plan's freshness gate cut the window." + } + }, + "required": [ + "after", + "before" + ], + "description": "The publication window the answer covers, [after, before) in UTC, normalized from after and before." + }, "RecommendationListResponse": { "type": "object", "properties": { - "data": { + "recommendations": { "type": "array", "items": { "$ref": "#/components/schemas/Recommendation" - } + }, + "description": "Newest first." }, "has_more": { "type": "boolean", @@ -2623,13 +2350,17 @@ "description": "The resolved entity the recommendations belong to." } ] + }, + "window": { + "$ref": "#/components/schemas/PublicationWindow" } }, "required": [ - "data", + "recommendations", "has_more", "next_cursor", - "entity" + "entity", + "window" ] }, "Recommendation": { @@ -2639,13 +2370,14 @@ "type": "string", "description": "Public recommendation id in the form \"com_{n}\"." }, - "recommendation_id": { - "type": "integer", - "description": "Raw integer id of the recommendation row. Same number as in the \"com_{n}\" public id." - }, - "mention_class": { + "class": { "type": "string", - "description": "Commercial mention classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention)." + "enum": [ + "sponsored", + "organic", + "mention" + ], + "description": "Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention)." }, "entity": { "$ref": "#/components/schemas/EntityRef" @@ -2711,29 +2443,19 @@ "source_channel" ] }, - "start_timestamp": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds." - }, - "end_timestamp": { - "type": "string", - "deprecated": true, - "description": "DEPRECATED: prefer the numeric sibling field. This \"MM:SS\" (or \"HH:MM:SS\") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds." - }, "start_seconds": { "type": [ "integer", "null" ], - "description": "Start position in the video in integer SECONDS, parsed from start_timestamp. Prefer this over the deprecated string field. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "Start position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." }, "end_seconds": { "type": [ "integer", "null" ], - "description": "End position in the video in integer SECONDS, parsed from end_timestamp. 0 means \"full episode / no specific moment\" (the string sentinel \"00:00\"). Null when the string timestamp is null or unparseable." + "description": "End position in the video in integer seconds. 0 means \"full episode / no specific moment\". Null when the analyzer could not place it in time." }, "verbatim_quote": { "type": [ @@ -2796,12 +2518,9 @@ }, "required": [ "id", - "recommendation_id", - "mention_class", + "class", "entity", "media", - "start_timestamp", - "end_timestamp", "start_seconds", "end_seconds", "verbatim_quote", @@ -2819,17 +2538,17 @@ "type": "object", "properties": { "feedback_id": { - "type": "integer", - "description": "Id of the persisted feedback record. Read it back via GET /v1/feedback/{feedback_id}." + "type": "string", + "description": "Id of the persisted feedback record, fbk_ and digits. Read it back via GET /v1/feedback/{feedback_id}." }, "type": { "type": "string", - "description": "The feedback type you submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search." + "description": "The feedback type you submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search, experience." }, "query": { "type": "object", "additionalProperties": {}, - "description": "The query object the feedback is attached to, echoed back." + "description": "The query object the feedback is attached to, echoed back. category and mcp_call_id, when sent, are recorded in it under those names." }, "applied": { "type": "integer", @@ -2888,21 +2607,6 @@ ], "description": "Outcome. Values: applied (the correction was applied automatically), unchanged (the target already had the requested value), not_found (the target does not exist), invalid (the correction payload was malformed for its kind), logged (recorded for human review; no automatic apply)." }, - "previous_mention_class": { - "type": [ - "string", - "null" - ], - "description": "The mention_class before the correction. Only present for recommendation-class corrections." - }, - "new_mention_class": { - "type": "string", - "description": "The mention_class requested. Only present for recommendation-class corrections." - }, - "rows_affected": { - "type": "integer", - "description": "Rows updated by an applied correction." - }, "reason": { "type": "string", "description": "The reason code you supplied, echoed back." @@ -2910,16 +2614,6 @@ "message": { "type": "string", "description": "Human-readable explanation of the outcome." - }, - "recommendation": { - "allOf": [ - { - "$ref": "#/components/schemas/Recommendation" - }, - { - "description": "The recommendation after the correction. Only present for recommendation-class corrections that resolved a row." - } - ] } }, "required": [ @@ -2931,23 +2625,23 @@ "MergeSuggestionChange": { "type": "object", "properties": { - "sourceEntityId": { + "source_entity_id": { "type": "string", "description": "Public id (\"ent_{n}\") of the duplicate/variant entity to merge away." }, - "targetEntityId": { + "target_entity_id": { "type": "string", "description": "Public id (\"ent_{n}\") of the canonical entity to merge into." }, - "sourceName": { + "source_name": { "type": "string", "description": "Name of the duplicate entity when you do not have its id." }, "merge_into": { "type": "string", - "description": "Name or public id of the canonical entity when you do not have targetEntityId." + "description": "Name or public id of the canonical entity when you do not have target_entity_id." }, - "scopeType": { + "scope_type": { "type": "string", "description": "Scope of the merge rule, e.g. \"global\"." } @@ -3014,14 +2708,14 @@ "WrongClassificationChange": { "type": "object", "properties": { - "mention_class": { + "class": { "type": "string", "enum": [ - "ad_read", - "endorsement", + "sponsored", + "organic", "mention" ], - "description": "The correct commercial classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention)." + "description": "The correct commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention)." } }, "additionalProperties": {}, @@ -3110,12 +2804,12 @@ "type": "object", "properties": { "feedback_id": { - "type": "integer", - "description": "Id of the feedback record." + "type": "string", + "description": "Id of the feedback record, fbk_ and digits." }, "type": { "type": "string", - "description": "The feedback type as submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search." + "description": "The feedback type as submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search, experience." }, "status": { "type": "string", @@ -3184,7 +2878,7 @@ "string", "null" ], - "description": "The issue_type as submitted. Null when the correction carried only a reason or mention_class." + "description": "The issue_type as submitted. Null when the correction carried only a reason or class." }, "reason": { "type": [ @@ -3429,6 +3123,67 @@ "type": "integer", "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." }, + "details": { + "type": "object", + "properties": { + "quote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." + }, + "existing_request_id": { + "type": "string", + "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + }, + "existing_id": { + "type": "string", + "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." + } + }, + "description": "Machine data the refusal carries for you to act on. Present only on the codes that name a field here." + }, "doc_url": { "type": "string" }, @@ -3544,43 +3299,31 @@ "type": "string", "description": "The q parameter echoed back." }, - "requestedK": { + "limit": { "type": "integer", "description": "The limit applied." }, - "returnedN": { + "returned": { "type": "integer", "description": "Chunks returned." }, "filters": { "type": "object", "properties": { - "channelIds": { + "channel_ids": { "type": "array", "items": { "type": "string" }, "description": "Channel ids the search was scoped to, after entity_ids were expanded." }, - "entityIds": { + "entity_ids": { "type": "array", "items": { "type": "string" }, "description": "Exact explicit entity_ids accepted for this search. Every id was resolved; an unknown id is refused." }, - "publishedAfter": { - "type": [ - "string", - "null" - ] - }, - "publishedBefore": { - "type": [ - "string", - "null" - ] - }, "about": { "type": "array", "items": { @@ -3598,21 +3341,27 @@ "kind": { "type": "array", "items": { - "type": "string" + "type": "string", + "enum": [ + "sponsored", + "organic", + "mention" + ] }, - "description": "The kind values applied." + "description": "The passage classes applied." } }, "required": [ - "channelIds", - "entityIds", - "publishedAfter", - "publishedBefore", + "channel_ids", + "entity_ids", "about", "by", "kind" ] }, + "window": { + "$ref": "#/components/schemas/PublicationWindow" + }, "chunks": { "type": "array", "items": { @@ -3624,7 +3373,7 @@ "type": "boolean", "description": "True when some retrieval batches failed and these chunks are what survived." }, - "failedBatches": { + "failed_batches": { "type": "integer", "description": "How many batches failed when partial is true." }, @@ -3633,7 +3382,7 @@ "string", "null" ], - "description": "Newest publishedAt among the chunks. Null when there are none." + "description": "Newest published_at among the chunks. Null when there are none." }, "search_index": { "type": "object", @@ -3773,6 +3522,67 @@ "type": "integer", "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." }, + "details": { + "type": "object", + "properties": { + "quote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." + }, + "existing_request_id": { + "type": "string", + "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + }, + "existing_id": { + "type": "string", + "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." + } + }, + "description": "Machine data the refusal carries for you to act on. Present only on the codes that name a field here." + }, "doc_url": { "type": "string" }, @@ -3789,6 +3599,24 @@ ], "description": "The gate that reduced this response. Present only when something was withheld; carries the same code, gate, and unlock an outright refusal would." }, + "unlock": { + "type": "object", + "properties": { + "tier": { + "type": "string", + "description": "The plan that lifts the freshness gate." + }, + "url": { + "type": "string", + "description": "Where to start that plan." + } + }, + "required": [ + "tier", + "url" + ], + "description": "Present when your plan's freshness gate cut the window and nothing older matched; note says so." + }, "note": { "type": "string", "description": "One steering sentence for the agent reading this." @@ -3796,9 +3624,10 @@ }, "required": [ "query", - "requestedK", - "returnedN", + "limit", + "returned", "filters", + "window", "chunks", "as_of", "search_index", @@ -3806,15 +3635,13 @@ ], "example": { "query": "Ramp corporate cards", - "requestedK": 5, - "returnedN": 1, + "limit": 5, + "returned": 1, "filters": { - "channelIds": [ + "channel_ids": [ "UC-DRzaGnL_vtBUpCFH5M0tg" ], - "entityIds": [], - "publishedAfter": null, - "publishedBefore": null, + "entity_ids": [], "about": [ { "id": "ent_14", @@ -3824,26 +3651,30 @@ ], "by": [], "kind": [ - "recommendation_sponsored" + "sponsored" ] }, + "window": { + "after": "2026-08-01T00:00:00Z", + "before": null + }, "chunks": [ { "id": "UC-DRzaGnL_vtBUpCFH5M0tg/2026-08-04/dQw4w9WgXcQ.md#12", - "videoId": "dQw4w9WgXcQ", - "channelId": "UC-DRzaGnL_vtBUpCFH5M0tg", - "channelName": "TBPN", - "videoTitle": "TBPN | Tuesday, August 4", + "video_id": "dQw4w9WgXcQ", + "channel_id": "UC-DRzaGnL_vtBUpCFH5M0tg", + "channel_name": "TBPN", + "video_title": "TBPN | Tuesday, August 4", "speakers": [ "John Coogan" ], "source": "creator_captions", - "sourceLabel": "Creator captions", - "publishedAt": "2026-08-04T17:00:00.000Z", + "source_label": "Creator captions", + "published_at": "2026-08-04T17:00:00.000Z", "text": "Ramp has been on the show for a while now and the pitch is still the same, spend less time on expenses.", - "startSeconds": 4787, - "watchUrl": "/watch?v=dQw4w9WgXcQ&t=4787", - "citeLine": "[TBPN | Tuesday, August 4](/watch?v=dQw4w9WgXcQ&t=4787) · @1:19:47 · TBPN · Aug 4, 2026", + "start_seconds": 4787, + "watch_url": "/watch?v=dQw4w9WgXcQ&t=4787", + "cite_line": "[TBPN | Tuesday, August 4](/watch?v=dQw4w9WgXcQ&t=4787) · @1:19:47 · TBPN · Aug 4, 2026", "score": 0.71, "about": [ { @@ -3866,7 +3697,7 @@ "state": "live", "missing_before": null }, - "note": "Quote text as a spoken beat of a few sentences and cite watchUrl with publishedAt. Results are recency-boosted; state the window from filters.publishedAfter when you passed one." + "note": "Quote text as a spoken beat of a few sentences and cite watch_url with published_at. Results are recency-boosted; state the window from window.after when you passed one." } }, "NamedEntityRef": { @@ -3904,32 +3735,32 @@ "type": "string", "description": "Search index chunk id. Opaque." }, - "videoId": { + "video_id": { "type": "string", "description": "YouTube video id (11 characters)." }, - "channelId": { + "channel_id": { "type": [ "string", "null" ], "description": "YouTube channel id of the source channel." }, - "channelName": { + "channel_name": { "type": [ "string", "null" ], "description": "Source channel name." }, - "channelPage": { + "channel_page": { "type": [ "string", "null" ], "description": "The channel's page on arcmira.com, absolute. Null when no channel is known." }, - "videoTitle": { + "video_title": { "type": [ "string", "null" @@ -3950,14 +3781,14 @@ ], "description": "Transcript source class: arcmira_premium, creator_captions, or third_party_quick." }, - "sourceLabel": { + "source_label": { "type": [ "string", "null" ], "description": "Human label for source." }, - "publishedAt": { + "published_at": { "type": [ "string", "null" @@ -3968,22 +3799,18 @@ "type": "string", "description": "The spoken slice. Search results include text on every plan within the permitted publication-date window." }, - "textWithheld": { - "type": "boolean", - "description": "Legacy field, no longer set. Since 2026-09-10, transcript search includes spoken text on every plan and limits results by publication date." - }, - "startSeconds": { + "start_seconds": { "type": [ "integer", "null" ], "description": "Offset of the slice in the video, in seconds." }, - "watchUrl": { + "watch_url": { "type": "string", "description": "Site-relative watch URL with the timestamp, e.g. /watch?v=...&t=4787." }, - "citeLine": { + "cite_line": { "type": [ "string", "null" @@ -4011,13 +3838,13 @@ }, "required": [ "id", - "videoId", - "channelId", + "video_id", + "channel_id", "source", - "publishedAt", + "published_at", "text", - "startSeconds", - "watchUrl", + "start_seconds", + "watch_url", "score" ] }, @@ -4257,11 +4084,72 @@ "type": "integer", "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." }, - "doc_url": { - "type": "string" - }, - "request_id": { - "type": "string" + "details": { + "type": "object", + "properties": { + "quote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." + }, + "existing_request_id": { + "type": "string", + "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + }, + "existing_id": { + "type": "string", + "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." + } + }, + "description": "Machine data the refusal carries for you to act on. Present only on the codes that name a field here." + }, + "doc_url": { + "type": "string" + }, + "request_id": { + "type": "string" } }, "required": [ @@ -4545,6 +4433,9 @@ ], "description": "Signed continuation for the next page. Null on the last page." }, + "window": { + "$ref": "#/components/schemas/PublicationWindow" + }, "indexed_through": { "type": [ "string", @@ -4577,6 +4468,7 @@ "returned", "has_more", "next_cursor", + "window", "indexed_through", "index_age_days", "as_of", @@ -4604,6 +4496,10 @@ ], "returned": 1, "has_more": true, + "window": { + "after": "2026-08-01T00:00:00Z", + "before": null + }, "indexed_through": "2026-08-28T17:00:00.000Z", "index_age_days": 3, "as_of": "2026-08-28T17:00:00.000Z", @@ -4622,26 +4518,17 @@ ], "description": "The mode applied." }, - "publishedAfter": { - "type": [ - "string", - "null" - ] - }, - "publishedBefore": { - "type": [ - "string", - "null" - ] + "window": { + "$ref": "#/components/schemas/PublicationWindow" }, - "channelIds": { + "channel_ids": { "type": "array", "items": { "type": "string" }, "description": "The channel ids counted." }, - "videoIds": { + "video_ids": { "type": "array", "items": { "type": "string" @@ -4877,10 +4764,9 @@ }, "required": [ "mode", - "publishedAfter", - "publishedBefore", - "channelIds", - "videoIds", + "window", + "channel_ids", + "video_ids", "rows", "returned", "has_more", @@ -4890,13 +4776,15 @@ ], "example": { "mode": "mentions", - "publishedAfter": "2026-06-01", - "publishedBefore": null, - "channelIds": [ + "window": { + "after": "2026-06-01T00:00:00Z", + "before": null + }, + "channel_ids": [ "UC-DRzaGnL_vtBUpCFH5M0tg", "UClWkDGXEzsh77GAhs90wpXw" ], - "videoIds": [], + "video_ids": [], "rows": [ { "entity_id": "ent_14", @@ -4963,15326 +4851,1835 @@ "note": "Catalog counts, all-time unless you passed a window. shared ranks true overlap by the smallest per-channel count. Use search_transcripts afterwards only for quotes." } }, - "PersonPageResponse": { + "MonitorListResponse": { "type": "object", "properties": { - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Person name." - }, - "type": { - "type": "string", - "description": "Stored entity type. Always person." - }, - "platform": { - "type": [ - "string", - "null" - ], - "description": "Source platform. Null for people." - }, - "url": { - "type": [ - "string", - "null" - ], - "description": "External URL for the person. Null when none is known." - }, - "created_at": { - "type": "string", - "description": "When the entity row was created." - }, - "image_url": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until resolved." - }, - "image_checked_at": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "merged_into_entity_id": { - "type": [ - "integer", - "null" - ], - "description": "Raw id of the entity this one was merged into. Null on a canonical entity, which a page always serves." - }, - "owner_entity_id": { - "type": [ - "integer", - "null" - ], - "description": "Raw id of the owning entity. Null unless an ownership link exists." - }, - "youtube_channel_id": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id. Null for people." - }, - "youtube_handle": { - "type": [ - "string", - "null" - ], - "description": "YouTube handle. Null for people." - }, - "is_priority": { - "type": [ - "integer", - "null" - ], - "description": "1 when the entity is flagged priority, 0 or null otherwise." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Same value as image_url." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "Same value as image_checked_at." - }, - "ownedChannels": { - "type": [ - "array", - "null" - ], - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Entity name." - }, - "display_name": { - "type": "string", - "description": "Label that tells a homonym apart, e.g. \"AdQuick (channel)\". Equals name when no label is needed." - }, - "type": { - "type": "string", - "description": "Entity type: channel or product." - }, - "route": { - "type": "string", - "description": "Site-relative route of the entity page on arcmira.com." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Entity image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "videoCount": { - "type": "integer", - "description": "Channels only: media rows published by the channel." - }, - "mentionCount": { - "type": "integer", - "description": "Products only: appearance rows of the product." - } - }, - "required": [ - "id", - "name", - "display_name", - "type", - "route", - "imageUrl", - "imageCheckedAt" - ] + "monitors": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/Monitor" }, - "description": "Up to 10 channels this entity owns, most videos first. Null when it owns none." - }, - "ownedProducts": { - "type": [ - "array", - "null" - ], - "items": { + { "type": "object", "properties": { - "id": { + "tracker_count": { "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Entity name." - }, - "display_name": { - "type": "string", - "description": "Label that tells a homonym apart, e.g. \"AdQuick (channel)\". Equals name when no label is needed." - }, - "type": { - "type": "string", - "description": "Entity type: channel or product." + "description": "Number of trackers in the monitor." }, - "route": { - "type": "string", - "description": "Site-relative route of the entity page on arcmira.com." + "alerts_this_month": { + "type": "integer", + "description": "Alert deliveries written for this monitor since the start of the calendar month." }, - "imageUrl": { + "slack_integration": { "type": [ - "string", + "object", "null" ], - "description": "Entity image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" + "properties": { + "id": { + "type": "string", + "description": "Slack integration id." + }, + "team_name": { + "type": [ + "string", + "null" + ], + "description": "Slack workspace name." + }, + "channel_name": { + "type": [ + "string", + "null" + ], + "description": "Slack channel the integration posts to." + } + }, + "required": [ + "id", + "team_name", + "channel_name" ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "videoCount": { - "type": "integer", - "description": "Channels only: media rows published by the channel." - }, - "mentionCount": { - "type": "integer", - "description": "Products only: appearance rows of the product." + "description": "Display metadata for the connected Slack integration. Null/absent when Slack is not configured." } }, "required": [ - "id", - "name", - "display_name", - "type", - "route", - "imageUrl", - "imageCheckedAt" + "tracker_count", + "alerts_this_month" ] - }, - "description": "Up to 10 products this entity owns, most mentions first. Null when it owns none." - } + } + ] }, - "required": [ - "id", - "name", - "type", - "platform", - "url", - "created_at", - "image_url", - "image_checked_at", - "merged_into_entity_id", - "owner_entity_id", - "youtube_channel_id", - "youtube_handle", - "is_priority", - "imageUrl", - "imageCheckedAt", - "ownedChannels", - "ownedProducts" - ], - "description": "The person: the stored entity row plus camelCase image fields and what the person owns." + "description": "The account's personal monitors and the monitors of every team it belongs to, ordered by dashboard sort position, then name." + } + }, + "required": [ + "monitors" + ] + }, + "Monitor": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Monitor id." }, - "roleEdge": { - "type": [ - "object", - "null" - ], - "properties": { - "role": { - "type": "string", - "enum": [ - "ceo" - ], - "description": "Always ceo." - }, - "label": { - "type": "string", - "enum": [ - "CEO of", - "CO-CEO of" - ], - "description": "Display label for the role line." - }, - "name": { - "type": "string", - "description": "Name of the organization the person leads." - }, - "href": { - "type": [ - "string", - "null" - ], - "description": "Site-relative link to the organization page. Null when none." - }, - "receipt": { - "type": [ - "string", - "null" - ], - "description": "Display line naming the evidence for the role. Null when there is none to show." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Organization logo URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked the organization. Null until checked." - } - }, - "required": [ - "role", - "label", - "name", - "href", - "receipt", - "imageUrl", - "imageCheckedAt" - ], - "description": "A verified CEO role for the person. Null when none is verified." - }, - "stats": { - "type": "object", - "properties": { - "velocity": { - "type": "integer", - "description": "Appearances in the last 90 days." - }, - "sentiment": { - "type": "number", - "description": "Reserved. Always 0." - }, - "reach": { - "type": "string", - "description": "Total views of the person's appearances as display text, e.g. 1.2M." - }, - "reachRaw": { - "type": "number", - "description": "Total views of the person's appearances." - }, - "total": { - "type": "integer", - "description": "Total appearances." - }, - "totalMentions": { - "type": "integer", - "description": "Total media that mention the person." - }, - "totalMentionViews": { - "type": "number", - "description": "Total views of the media that mention the person." - }, - "mentionReach": { - "type": "string", - "description": "totalMentionViews as display text." - }, - "latestMediaAt": { - "type": [ - "string", - "null" - ], - "description": "Newest publish date among the counted media. Null when none. The freshness gate does not withhold it." - } - }, - "required": [ - "velocity", - "sentiment", - "reach", - "reachRaw", - "total", - "totalMentions", - "totalMentionViews", - "mentionReach", - "latestMediaAt" - ], - "description": "Headline numbers for the person." + "name": { + "type": "string", + "description": "Monitor name." }, - "appearancesByMonth": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] - }, - "description": "Appearances per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." + "paused": { + "type": "boolean", + "description": "True when delivery is paused for all trackers in this monitor. New alerts are not queued, and queued email delivery checks the pause state again before sending. Use PATCH /v1/trackers/{id} with paused: true to pause one tracker." }, - "mentionsByMonth": { + "notify_emails": { "type": "array", "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] + "type": "string" }, - "description": "Media mentioning the person per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." + "description": "Configured email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor." }, - "topics": { + "email_recipients": { "type": "array", "items": { "type": "object", "properties": { - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." + "email": { + "type": "string" }, - "sentiment": { + "role": { "type": "string", "enum": [ - "positive", - "neutral", - "negative" + "owner", + "member", + "external" ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "name", - "count", - "sentiment" - ] - }, - "description": "Topics in the media the person appeared in, highest count first." - }, - "people": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Person name." + "description": "owner: the paying account. member: a member of the monitor's team, who receives its alerts without an invitation and does not count toward the recipient limits. external: anyone else, who must confirm first." }, - "count": { + "user_id": { "type": [ - "integer", + "string", "null" ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." + "description": "The Arcmira user behind an owner or member address. Null for external recipients." }, - "sentiment": { + "status": { "type": "string", "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" + "active", + "pending", + "unsubscribed", + "suppressed", + "removed", + "owner_unverified", + "plan_limited", + "muted" ], - "description": "When the image pipeline last checked this entity. Null until checked." + "description": "muted: a team member muted this monitor for themselves." }, - "role": { + "invitation_status": { "type": "string", "enum": [ - "Connection" - ], - "description": "Always Connection." + "sent", + "failed", + "limited", + "pending" + ] } }, "required": [ - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt", - "role" + "email", + "role", + "user_id", + "status" ] }, - "description": "People in the media the person appeared in, highest count first." + "description": "Every address the monitor reaches, with consent and invitation state. An account is not required to accept." }, - "brands": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } + "notify_frequency": { + "type": "string", + "default": "realtime", + "description": "Delivery cadence. Values: realtime (deliver immediately), hourly (hourly digest), daily (daily digest). Free tier is limited to daily." + }, + "digest_day": { + "type": "string", + "default": "monday", + "description": "Day of week for digest delivery." + }, + "digest_time": { + "type": "string", + "default": "09:00", + "description": "Time of day (HH:MM) for digest delivery." + }, + "notify_webhook": { + "type": "boolean", + "description": "True when webhook delivery is enabled." + }, + "webhook_url": { + "type": [ + "string", + "null" + ], + "description": "Webhook destination URL. Null when no webhook is configured. Absent when access is member: only the team owner sees the webhook." + }, + "webhook_secret_set": { + "type": "boolean", + "description": "True when a webhook signing secret exists for this monitor. The secret itself is never returned on reads; enablement and rotation responses support recovery with the original Idempotency-Key during the valid recovery window. Absent when access is member." + }, + "webhook_secret_hint": { + "type": [ + "string", + "null" + ], + "description": "Last 4 characters of the current signing secret, for identifying which secret you hold. Null until a secret exists. Absent when access is member." + }, + "webhook_failures": { + "type": "integer", + "default": 0, + "description": "Consecutive webhook delivery failures recorded for this monitor. Reset by a secret rotation or PATCHing notify_webhook: true; 10 consecutive failures auto-disable a webhook. Note: the delivery pipeline currently accrues failures on the tracker that fired, so this monitor-level counter can lag." + }, + "webhook_disabled_at": { + "type": [ + "string", + "null" + ], + "description": "When the webhook was auto-disabled after repeated failures. Null while delivery is enabled. Re-enable by PATCHing notify_webhook: true; rotation alone never re-enables." + }, + "webhook_disabled_reason": { + "type": [ + "string", + "null" + ], + "description": "Why the webhook was auto-disabled. Null while delivery is enabled." + }, + "notify_slack": { + "type": "boolean", + "description": "True when Slack delivery is enabled." + }, + "slack_integration_id": { + "type": [ + "string", + "null" + ], + "description": "Slack integration used for delivery. Null when Slack is not configured." + }, + "slack_channel_id": { + "type": [ + "string", + "null" + ], + "description": "Slack channel to deliver to. Null when Slack is not configured." + }, + "created_at": { + "type": "string", + "description": "When the monitor was created." + }, + "updated_at": { + "type": "string", + "description": "When the monitor was last updated." + }, + "team": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": "string", + "description": "Team id." }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] + "name": { + "type": "string", + "description": "Team name." + } }, - "description": "Organizations in the media the person appeared in, highest count first." + "required": [ + "id", + "name" + ], + "description": "The team the monitor is shared with. Null for a personal monitor." }, - "products": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } + "access": { + "type": "string", + "enum": [ + "account", + "member" + ], + "description": "account: the caller pays for the monitor, as its personal owner or the team owner. member: the caller is another member of its team, who may edit it but not its webhook, and may not delete it." + }, + "muted": { + "type": "boolean", + "description": "True when the caller muted this team monitor for themselves. Always false on a personal monitor." + } + }, + "required": [ + "id", + "name", + "paused", + "notify_emails", + "notify_webhook", + "webhook_disabled_at", + "webhook_disabled_reason", + "notify_slack", + "slack_integration_id", + "slack_channel_id", + "created_at", + "updated_at", + "team", + "access", + "muted" + ] + }, + "MonitorMutationResponse": { + "type": "object", + "properties": { + "monitor": { + "allOf": [ + { + "$ref": "#/components/schemas/Monitor" }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Products in the media the person appeared in, highest count first." + { + "type": "object", + "properties": { + "tracker_count": { + "type": "integer", + "description": "Number of trackers in the monitor. Always 0 in the create response." + }, + "webhook_secret": { + "type": "string", + "description": "The webhook signing secret (\"whsec_...\"). Only present when this request NEWLY enabled webhook signing: a create with notify_webhook: true and a webhook_url, or a PATCH that turns the webhook on (or sets a URL) where no secret existed before. Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again." + } + }, + "required": [ + "tracker_count" + ] + } + ] + }, + "message": { + "type": "string", + "description": "Human-readable confirmation." + } + }, + "required": [ + "monitor", + "message" + ] + }, + "WebhookSecretRotateResponse": { + "type": "object", + "properties": { + "webhook_secret": { + "type": "string", + "description": "The NEW webhook signing secret (\"whsec_...\"). Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again." + }, + "webhook_secret_hint": { + "type": "string", + "description": "Last 4 characters of the new secret, for identifying which secret you hold." + }, + "previous_secret_expires_at": { + "type": [ + "string", + "null" + ], + "description": "End of the 24-hour overlap window. Until then, deliveries carry an additional X-Arcmira-Signature-Previous header computed with the previous secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. Null when the monitor had no previous secret (nothing to overlap)." + } + }, + "required": [ + "webhook_secret", + "webhook_secret_hint", + "previous_secret_expires_at" + ] + }, + "MonitorDeleteResponse": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Human-readable confirmation." }, - "appearances": { + "trackers_deleted": { + "type": "integer", + "description": "Number of trackers that were deleted along with the monitor." + } + }, + "required": [ + "message", + "trackers_deleted" + ] + }, + "MonitorTrackersResponse": { + "type": "object", + "properties": { + "trackers": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string", - "description": "Raw appearance row id (the first row for the video), as a string." + "description": "Tracker id." }, - "date": { + "entity_name": { "type": "string", - "description": "Publish date as locale display text, or Unknown." - }, - "publishedAt": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." + "description": "Tracked entity name." }, - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title." + "entity_type": { + "type": "string", + "description": "Tracked entity type." }, - "channel": { + "display_name": { "type": "string", - "description": "Source channel name, or Unknown Channel." + "description": "User-facing display name. Falls back to entity_name when not customized." }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the source channel. Null when unknown." + "paused": { + "type": "boolean", + "description": "True when the tracker is paused." }, - "channelHandle": { + "paused_at": { "type": [ "string", "null" ], - "description": "YouTube handle of the source channel. Null when unknown." - }, - "platform": { - "type": "string", - "enum": [ - "youtube" - ], - "description": "Always youtube." - }, - "thumbnail": { - "type": "string", - "description": "Video thumbnail URL." - }, - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL. Same value as thumbnail." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." + "description": "When the tracker was paused. Null unless paused." }, - "duration": { + "last_notified_at": { "type": [ - "number", + "string", "null" ], - "description": "Video length in seconds. Null when unknown." - }, - "type": { - "type": "string", - "enum": [ - "host", - "guest", - "mention" - ], - "description": "host or guest on an appearance row (host when the person hosts the channel); mention on a mention row." - }, - "context": { - "type": "string", - "description": "Description of the moment, or \"No description available\"." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." + "description": "When the tracker last produced an alert. Null until the first alert." }, - "timestamp": { + "created_at": { "type": "string", - "description": "Earliest start timestamp as MM:SS text, or \"Full Episode\" when none." + "description": "When the tracker was created." }, - "rawTimestamp": { + "updated_at": { "type": [ "string", "null" ], - "description": "Earliest start timestamp as stored. Null when none." + "description": "When the tracker was last updated." }, - "excerpt": { - "$ref": "#/components/schemas/PublishedExcerpt" + "monitor_id": { + "type": "string", + "description": "The monitor id from the request path." } }, "required": [ "id", - "date", - "publishedAt", - "title", - "channel", - "channelId", - "channelHandle", - "platform", - "thumbnail", - "thumbnailUrl", - "videoId", - "duration", - "type", - "context", - "sentiment", - "timestamp", - "rawTimestamp" + "entity_name", + "entity_type", + "display_name", + "paused", + "paused_at", + "last_notified_at", + "created_at", + "updated_at", + "monitor_id" ] }, - "description": "Newest media the person appeared in, one row per video, cut to the plan's media rows." + "description": "Trackers in the monitor, newest first." }, - "mentions": { + "count": { + "type": "integer", + "description": "Number of trackers returned." + } + }, + "required": [ + "trackers", + "count" + ] + }, + "AlertListResponse": { + "type": "object", + "properties": { + "alerts": { "type": "array", "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Raw appearance row id (the first row for the video), as a string." - }, - "date": { - "type": "string", - "description": "Publish date as locale display text, or Unknown." - }, - "publishedAt": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." - }, - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title." - }, - "channel": { - "type": "string", - "description": "Source channel name, or Unknown Channel." - }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the source channel. Null when unknown." - }, - "channelHandle": { - "type": [ - "string", - "null" - ], - "description": "YouTube handle of the source channel. Null when unknown." - }, - "platform": { - "type": "string", - "enum": [ - "youtube" - ], - "description": "Always youtube." - }, - "thumbnail": { - "type": "string", - "description": "Video thumbnail URL." - }, - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL. Same value as thumbnail." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "duration": { - "type": [ - "number", - "null" - ], - "description": "Video length in seconds. Null when unknown." - }, - "type": { - "type": "string", - "enum": [ - "host", - "guest", - "mention" - ], - "description": "host or guest on an appearance row (host when the person hosts the channel); mention on a mention row." - }, - "context": { - "type": "string", - "description": "Description of the moment, or \"No description available\"." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "timestamp": { - "type": "string", - "description": "Earliest start timestamp as MM:SS text, or \"Full Episode\" when none." - }, - "rawTimestamp": { - "type": [ - "string", - "null" - ], - "description": "Earliest start timestamp as stored. Null when none." - }, - "excerpt": { - "$ref": "#/components/schemas/PublishedExcerpt" - } - }, - "required": [ - "id", - "date", - "publishedAt", - "title", - "channel", - "channelId", - "channelHandle", - "platform", - "thumbnail", - "thumbnailUrl", - "videoId", - "duration", - "type", - "context", - "sentiment", - "timestamp", - "rawTimestamp" - ] + "$ref": "#/components/schemas/Alert" }, - "description": "Newest media that mention the person without them appearing, one row per video." + "description": "Newest alerts first." + }, + "has_more": { + "type": "boolean", + "description": "True when older alerts exist past limit. The endpoint does not paginate: raise limit, up to 100, to read them." }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" + "next_cursor": { + "type": "null", + "description": "Always null: this endpoint does not paginate." } }, "required": [ - "entity", - "roleEdge", - "stats", - "appearancesByMonth", - "mentionsByMonth", - "topics", - "people", - "brands", - "products", - "appearances", - "mentions", - "_meta" + "alerts", + "has_more", + "next_cursor" ] }, - "PublishedExcerpt": { + "Alert": { "type": "object", "properties": { "id": { "type": "string", - "description": "Published excerpt id." - }, - "exactText": { - "type": "string", - "description": "The transcript span that names the entity." + "description": "Alert delivery id." }, - "contextBefore": { - "type": "string", - "description": "Transcript text immediately before the span." - }, - "contextAfter": { - "type": "string", - "description": "Transcript text immediately after the span." - }, - "term": { - "type": "string", - "description": "The surface form that matched in the transcript, which may be an approved alias." - }, - "termCharStart": { - "type": "integer", - "description": "Character offset where term starts." - }, - "termCharEnd": { - "type": "integer", - "description": "Character offset where term ends." - }, - "startSeconds": { - "type": "number", - "description": "Span start in the video, in seconds." - }, - "endSeconds": { - "type": "number", - "description": "Span end in the video, in seconds." - }, - "publicSourceClass": { - "type": "string", - "enum": [ - "arcmira_premium_excerpt", - "creator_captions", - "third_party_quick" + "tracker_id": { + "type": [ + "string", + "null" ], - "description": "Transcript source class of the excerpt." - } - }, - "required": [ - "id", - "exactText", - "term", - "termCharStart", - "termCharEnd", - "startSeconds", - "publicSourceClass" - ], - "description": "A speakerless excerpt naming the entity in this media, when one is active. Mention rows only." - }, - "ExposureMeta": { - "type": "object", - "properties": { - "tier": { - "type": "string", - "description": "Plan tier of the caller. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise." - }, - "isAuthenticated": { - "type": "boolean", - "description": "True when the request carried a valid credential." - }, - "rowsUsed": { - "type": "number", - "description": "Rows consumed this period, or lifetime rows on the free tier." - }, - "rowsRemaining": { - "type": "number", - "description": "Rows left this period, or lifetime rows left on the free tier." - }, - "monthlyRows": { - "type": "number", - "description": "Rows included per period, or the lifetime allocation on the free tier." - }, - "isOverLimit": { - "type": "boolean", - "description": "True when rowsUsed has reached monthlyRows." + "description": "Id of the tracker (tracked entity) that produced the alert." }, - "onDemandEnabled": { - "type": "boolean", - "description": "True when on-demand usage past the included rows is enabled." + "monitor_id": { + "type": [ + "string", + "null" + ], + "description": "Id of the monitor the tracker belongs to. Null for trackers outside a monitor." }, - "spendLimitCents": { - "type": "number", - "description": "On-demand spend limit in US cents. 0 means unlimited." + "entity_id": { + "type": [ + "string", + "null" + ], + "description": "Public id (\"ent_{n}\") of the entity that triggered the alert, when recorded. Null on older rows that were written before entity_id was stored on alert_delivery. Resolve the entity through mention_id or the embedded tracker when this is null." }, - "currentSpendCents": { - "type": "number", - "description": "On-demand spend so far this period, in US cents." + "mention_id": { + "type": [ + "string", + "null" + ], + "description": "Public id (\"men_{n}\") of the mention/appearance row that triggered the alert. Joins directly against mention rows (e.g. /v1/mentions). Null when not appearance-scoped." }, - "canContinue": { - "type": "boolean", - "description": "True when the caller can keep reading data right now." + "video_id": { + "type": [ + "string", + "null" + ], + "description": "YouTube video id (11 characters) of the video that triggered the alert. Null when not media-scoped." }, - "showingFullData": { - "type": "boolean", - "description": "True when the plan serves full data. False when the response is a preview: counts are null and lists are cut." + "excerpt_id": { + "type": [ + "string", + "null" + ], + "description": "Id of the active mention excerpt used as mention evidence. Null when the alert was sent before evidence was recorded." }, - "usageLimitType": { - "type": "string", + "evidence_kind": { + "type": [ + "string", + "null" + ], "enum": [ - "lifetime", - "monthly" + "excerpt", + null ], - "description": "lifetime on the free tier, whose rows never reset. monthly on paid tiers." - }, - "lifetimeRowsUsed": { - "type": "number", - "description": "Free tier only: lifetime rows used. Absent on paid tiers." + "description": "Which evidence layer was sent. Null on older rows." }, - "lifetimeRowsAllocated": { - "type": "number", - "description": "Free tier only: lifetime rows allocated. Absent on paid tiers." + "channel": { + "type": "string", + "description": "Delivery channel. Values: email (sent by email), webhook (POSTed to the configured webhook URL), slack (sent to Slack)." }, - "limitAction": { + "status": { "type": "string", - "enum": [ - "upgrade_to_pro", - "upgrade_or_enable_ondemand", - "upgrade_or_increase_limit", - "enable_ondemand", - "increase_limit", - "contact_sales" + "description": "Delivery status. Values: pending (queued for delivery), sent (delivered), failed (delivery failed; see error_message), skipped (delivery intentionally skipped)." + }, + "final_status": { + "type": [ + "string", + "null" ], - "description": "Present only when the caller is over the limit and cannot continue. Names the fix: upgrade_to_pro, upgrade_or_enable_ondemand, upgrade_or_increase_limit, enable_ondemand, increase_limit, or contact_sales." + "description": "Terminal status after retries. Null while delivery is still in progress." }, - "limitMessage": { - "type": "string", - "description": "Human sentence for the limit banner. Present only alongside limitAction." + "error_message": { + "type": [ + "string", + "null" + ], + "description": "Error details for failed deliveries. Null unless delivery failed." }, - "limitCtaHref": { - "type": "string", - "description": "Site path for the limit banner button. Present only alongside limitAction." + "scheduled_at": { + "type": [ + "string", + "null" + ], + "description": "When delivery was scheduled. Null when delivered immediately." }, - "limitUpgradeTier": { - "type": "string", - "description": "The next tier that lifts the limit. Present only alongside limitAction when an upgrade exists. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise." + "sent_at": { + "type": [ + "string", + "null" + ], + "description": "When the alert was actually sent. Null until delivery succeeds." }, - "limitUpgradeTierName": { + "created_at": { "type": "string", - "description": "Display name of limitUpgradeTier." + "description": "When the alert row was created." }, - "credits": { + "tracker": { "type": "object", "properties": { - "available": { + "id": { "type": [ - "integer", + "string", "null" ], - "description": "Credits spendable now: plan, granted, purchased, and on-demand up to its cap. Null when nothing limits it." - }, - "plan": { - "type": "object", - "properties": { - "credits": { - "type": [ - "integer", - "null" - ], - "description": "Plan credits this month. Null when the plan has no limit." - }, - "used": { - "type": "integer", - "description": "Plan credits spent this month." - }, - "resets_at": { - "type": "string", - "description": "YYYY-MM-DD, the first day of next month (UTC), when plan credits reset." - } - }, - "required": [ - "credits", - "used", - "resets_at" - ] + "description": "Tracker id." }, - "granted": { - "type": "integer", - "description": "Credits left in granted lots that have not expired." + "entity_name": { + "type": [ + "string", + "null" + ], + "description": "Tracked entity name." }, - "purchased": { - "type": "integer", - "description": "Credits left in purchased top-ups." + "entity_type": { + "type": [ + "string", + "null" + ], + "description": "Tracked entity type." }, - "on_demand": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "True when on-demand credits are on." - }, - "cap_credits": { - "type": [ - "integer", - "null" - ], - "description": "The on-demand cap in credits, at $0.002 a credit. Null when uncapped or off." - }, - "used": { - "type": "integer", - "description": "On-demand credits spent this month." - } - }, - "required": [ - "enabled", - "cap_credits", - "used" - ] + "display_name": { + "type": [ + "string", + "null" + ], + "description": "User-facing display name for the tracker. Null when not customized." } }, "required": [ - "available", - "plan", - "granted", - "purchased", - "on_demand" + "id", + "entity_name", + "entity_type", + "display_name" ], - "description": "The month in credits (1 credit is $0.001; a row is 4 credits). Present only when the credits ledger decides access." - }, - "totals": { - "type": "object", - "properties": { - "appearances": { - "type": "integer", - "description": "Total media rows: appearances for a person, mentions for other types, episodes for a channel." - }, - "mentions": { - "type": "integer", - "description": "Person pages only: total media that mention the person." - }, - "topics": { - "type": "integer", - "description": "Total related topics." - }, - "people": { - "type": "integer", - "description": "Total related people." - }, - "brands": { - "type": "integer", - "description": "Total related organizations, under the legacy key brands." - }, - "products": { - "type": "integer", - "description": "Total related products." - }, - "channels": { - "type": "integer", - "description": "Total related channels." - }, - "organizations": { - "type": "integer", - "description": "Total related organizations (organizations list)." - }, - "guests": { - "type": "integer", - "description": "Channel only: total unique guests." - }, - "hosts": { - "type": "integer", - "description": "Channel only: total hosts." - } - }, - "description": "True totals behind the returned rows. Each response sets only the keys for its own sections." + "description": "The tracker the alert belongs to. Fields are null when the tracker row was deleted." }, - "limits": { - "type": "object", - "properties": { - "freeAppearances": { - "type": "integer", - "description": "Media rows an anonymous caller is served." - }, - "freeEntitiesPerType": { - "type": "integer", - "description": "Sidebar rows per entity type an anonymous caller is served." - }, - "webhooksEnabled": { - "type": "boolean", - "description": "True when the plan includes webhook delivery." - }, - "exportEnabled": { - "type": "boolean", - "description": "True when the plan includes export." - }, - "apiEnabled": { - "type": "boolean", - "description": "True when the plan includes API access." - } - }, - "required": [ - "freeAppearances", - "freeEntitiesPerType", - "webhooksEnabled", - "exportEnabled", - "apiEnabled" + "monitor": { + "type": [ + "object", + "null" ], - "description": "Anonymous row limits and the plan's feature flags." - }, - "costPerRowCents": { - "type": "number", - "description": "On-demand price per row in US cents, e.g. 0.4. 0 on tiers without on-demand." - }, - "isOverage": { - "type": "boolean", - "description": "True when this request billed rows past the included allowance." - }, - "freeLimit": { - "type": "object", "properties": { - "appearances": { - "type": "integer", - "description": "Same as limits.freeAppearances." + "id": { + "type": "string", + "description": "Monitor id." }, - "entities": { - "type": "integer", - "description": "Same as limits.freeEntitiesPerType." + "name": { + "type": [ + "string", + "null" + ], + "description": "Monitor name." } }, "required": [ - "appearances", - "entities" + "id", + "name" ], - "description": "The anonymous row limits again, under their older key." - }, - "recentPreview": { - "type": "object", - "properties": { - "windowDays": { - "type": "integer", - "description": "Days of newest media the plan withholds." - }, - "hiddenRecentCount": { - "type": "integer", - "description": "Media rows inside the withheld window." - }, - "freeAccountUnlockCount": { - "type": "integer", - "description": "Rows a free account would unlock. Present only for anonymous callers (freshness code 2)." - }, - "newestHiddenAt": { - "type": "string", - "description": "Newest publish date inside the withheld window. Absent when nothing is withheld." - }, - "teaserItems": { - "type": "array", - "items": { - "type": "object", - "properties": { - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL." - }, - "channelName": { - "type": "string", - "description": "Source channel name." - }, - "publishedAt": { - "type": "string", - "description": "Publish date of the withheld video." - }, - "duration": { - "type": [ - "string", - "null" - ], - "description": "Video length as display text. Null when unknown." - }, - "title": { - "type": "string", - "description": "Video title, truncated server side." - } - }, - "required": [ - "thumbnailUrl", - "channelName", - "publishedAt", - "duration", - "title" - ] - }, - "description": "Up to three of the newest withheld rows, with safe fields only." - }, - "subject": { - "type": "string", - "enum": [ - "appearances", - "mentions", - "items" - ], - "description": "Which list the withheld rows belong to." - }, - "experiment": { - "type": "string", - "enum": [ - "recent-intel-gate-copy-v1" - ], - "description": "The copy experiment this band feeds. Always recent-intel-gate-copy-v1." - } - }, - "required": [ - "windowDays", - "hiddenRecentCount", - "subject", - "experiment" - ], - "description": "Present when the plan withholds the newest media: how much is hidden and a few safe teaser rows." - }, - "recentPreviewMentions": { - "type": "object", - "properties": { - "windowDays": { - "type": "integer", - "description": "Days of newest media the plan withholds." - }, - "hiddenRecentCount": { - "type": "integer", - "description": "Media rows inside the withheld window." - }, - "freeAccountUnlockCount": { - "type": "integer", - "description": "Rows a free account would unlock. Present only for anonymous callers (freshness code 2)." - }, - "newestHiddenAt": { - "type": "string", - "description": "Newest publish date inside the withheld window. Absent when nothing is withheld." - }, - "teaserItems": { - "type": "array", - "items": { - "type": "object", - "properties": { - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL." - }, - "channelName": { - "type": "string", - "description": "Source channel name." - }, - "publishedAt": { - "type": "string", - "description": "Publish date of the withheld video." - }, - "duration": { - "type": [ - "string", - "null" - ], - "description": "Video length as display text. Null when unknown." - }, - "title": { - "type": "string", - "description": "Video title, truncated server side." - } - }, - "required": [ - "thumbnailUrl", - "channelName", - "publishedAt", - "duration", - "title" - ] - }, - "description": "Up to three of the newest withheld rows, with safe fields only." - }, - "subject": { - "type": "string", - "enum": [ - "appearances", - "mentions", - "items" - ], - "description": "Which list the withheld rows belong to." - }, - "experiment": { - "type": "string", - "enum": [ - "recent-intel-gate-copy-v1" - ], - "description": "The copy experiment this band feeds. Always recent-intel-gate-copy-v1." - } - }, - "required": [ - "windowDays", - "hiddenRecentCount", - "subject", - "experiment" - ], - "description": "Person pages only: the same band for the mentions list." - }, - "access": { - "type": "object", - "properties": { - "v": { - "type": "number", - "enum": [ - 1 - ], - "description": "Access block version. Always 1." - }, - "view": { - "type": "string", - "enum": [ - "full", - "preview" - ], - "description": "full when the plan serves everything; preview when something was cut." - }, - "cls": { - "type": "string", - "enum": [ - "live", - "warm", - "public", - "cold" - ], - "description": "Freshness class of the response. live serves the newest media; the others delay it." - }, - "ladder": { - "type": "string", - "enum": [ - "anonymous", - "free", - "usage_limit", - "unlocked" - ], - "description": "Where the caller stands on the gate ladder." - }, - "rows": { - "type": "object", - "properties": { - "media": { - "type": "object", - "properties": { - "served": { - "type": "integer", - "description": "Rows the backend returns for this section." - }, - "visible": { - "type": "integer", - "description": "Rows the site paints unobscured." - }, - "placeholders": { - "type": "integer", - "description": "Blurred rows the site paints after the visible ones." - } - }, - "required": [ - "served", - "visible", - "placeholders" - ], - "description": "Media rows." - }, - "topics": { - "type": "object", - "properties": { - "served": { - "type": "integer", - "description": "Rows the backend returns for this section." - }, - "visible": { - "type": "integer", - "description": "Rows the site paints unobscured." - }, - "placeholders": { - "type": "integer", - "description": "Blurred rows the site paints after the visible ones." - } - }, - "required": [ - "served", - "visible", - "placeholders" - ], - "description": "Topic sidebar rows." - }, - "entities": { - "type": "object", - "properties": { - "served": { - "type": "integer", - "description": "Rows the backend returns for this section." - }, - "visible": { - "type": "integer", - "description": "Rows the site paints unobscured." - }, - "placeholders": { - "type": "integer", - "description": "Blurred rows the site paints after the visible ones." - } - }, - "required": [ - "served", - "visible", - "placeholders" - ], - "description": "People, organization, product, and channel sidebar rows." - } - }, - "required": [ - "media", - "topics", - "entities" - ], - "description": "Rows served, visible, and blurred per section." - }, - "freshness": { - "type": "object", - "properties": { - "code": { - "type": "integer", - "description": "Delivery class code: 0 live, 1 to 3 delayed classes." - }, - "delayDays": { - "type": "integer", - "description": "Days of newest media withheld. 0 when live." - } - }, - "required": [ - "code", - "delayDays" - ], - "description": "The freshness gate applied." - }, - "chart": { - "type": "object", - "properties": { - "months": { - "type": "integer", - "description": "Months of timeline the plan serves." - } - }, - "required": [ - "months" - ], - "description": "The timeline the plan serves." - }, - "pagination": { - "type": "boolean", - "description": "True when the plan may page past the first page." - }, - "showCounts": { - "type": "boolean", - "description": "True when the plan serves counts. When false, row counts are null." - }, - "withheld": { - "type": "array", - "items": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": [ - "media_rows", - "fresh_media", - "sidebar_rows", - "counts", - "chart", - "pagination" - ], - "description": "What was withheld. Values: media_rows (media rows past a position), fresh_media (the newest media), sidebar_rows (sidebar rows past a position), counts (row counts), chart (the timeline), pagination (pages past the first)." - }, - "what": { - "type": "string", - "enum": [ - "media_rows", - "fresh_media", - "sidebar_rows", - "counts", - "chart", - "pagination" - ], - "description": "Repeats kind, for readers written against the older key." - }, - "beyondRow": { - "type": "integer", - "description": "media_rows and sidebar_rows only: rows past this position are withheld." - }, - "windowDays": { - "type": "integer", - "description": "fresh_media only: days of newest media withheld." - }, - "cutoff": { - "type": [ - "string", - "null" - ], - "description": "fresh_media only: the publish date cutoff. Null when none applies." - }, - "section": { - "type": "string", - "enum": [ - "topics", - "entities" - ], - "description": "sidebar_rows only: which sidebar section." - }, - "param": { - "type": [ - "string", - "null" - ], - "enum": [ - "offset", - "cursor", - null - ], - "description": "pagination only: the paging parameter that was refused. Null when none was sent." - } - }, - "required": [ - "kind", - "what" - ] - }, - "description": "Every resource the plan withheld from this response. Empty when view is full." - }, - "unlock": { - "type": [ - "object", - "null" - ], - "properties": { - "tier": { - "type": "string", - "description": "The tier that removes the gate. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise." - }, - "url": { - "type": "string", - "description": "Absolute upgrade or sign-up URL carrying its ?src= attribution." - }, - "src": { - "type": "string", - "enum": [ - "access-envelope", - "api-boundary", - "mcp-tool" - ], - "description": "The surface the unlock link attributes to." - }, - "limitAction": { - "type": [ - "string", - "null" - ], - "enum": [ - "upgrade_to_pro", - "upgrade_or_enable_ondemand", - "upgrade_or_increase_limit", - "enable_ondemand", - "increase_limit", - "contact_sales", - null - ], - "description": "The limit fix when the caller is over its limit. Null otherwise." - } - }, - "required": [ - "tier", - "url", - "src", - "limitAction" - ], - "description": "How to lift the gate. Null when view is full." - } - }, - "required": [ - "v", - "view", - "cls", - "ladder", - "rows", - "freshness", - "chart", - "pagination", - "showCounts", - "withheld", - "unlock" - ], - "description": "Structured access block: what the plan served and what it withheld." - }, - "_dc": { - "type": "integer", - "description": "Delivery class code the freshness filter ran at. Present only when the plan delays media and the response went through that filter." - } - }, - "required": [ - "tier", - "isAuthenticated", - "rowsUsed", - "rowsRemaining", - "monthlyRows", - "isOverLimit", - "onDemandEnabled", - "spendLimitCents", - "currentSpendCents", - "canContinue", - "showingFullData", - "usageLimitType", - "totals", - "limits", - "costPerRowCents", - "isOverage", - "freeLimit", - "access" - ], - "description": "Usage, plan limits, and the access block for this response: what the caller's plan served and what it withheld." - }, - "PersonAppearanceListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Raw appearance row id (the first row for the video), as a string." - }, - "date": { - "type": "string", - "description": "Publish date as locale display text, or Unknown." - }, - "dateRaw": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." - }, - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title." - }, - "channel": { - "type": "string", - "description": "Source channel name, or Unknown Channel." - }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the source channel. Null when unknown." - }, - "channelHandle": { - "type": [ - "string", - "null" - ], - "description": "YouTube handle of the source channel. Null when unknown." - }, - "platform": { - "type": "string", - "enum": [ - "youtube" - ], - "description": "Always youtube." - }, - "type": { - "type": "string", - "enum": [ - "host", - "guest", - "mention" - ], - "description": "host when the person hosts the channel, guest otherwise. mention only when the request passed is_appearance=false, which lists media that mention the person instead." - }, - "context": { - "type": "string", - "description": "Longest description of the appearance, or \"No description available\"." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "thumbnail": { - "type": "string", - "description": "Video thumbnail URL." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "timestamp": { - "type": "string", - "description": "Earliest start timestamp as MM:SS text, or \"Full Episode\" when none." - }, - "rawTimestamp": { - "type": [ - "string", - "null" - ], - "description": "Earliest start timestamp as stored. Null when none." - }, - "duration": { - "type": [ - "number", - "null" - ], - "description": "Video length in seconds. Null when unknown." - }, - "viewCount": { - "type": [ - "integer", - "null" - ], - "description": "Video view count at index time. Null when never fetched." - }, - "excerpt": { - "$ref": "#/components/schemas/PublishedExcerpt" - } - }, - "required": [ - "id", - "date", - "dateRaw", - "title", - "channel", - "channelId", - "channelHandle", - "platform", - "type", - "context", - "sentiment", - "thumbnail", - "videoId", - "timestamp", - "rawTimestamp", - "duration", - "viewCount" - ] - }, - "description": "Media the person appeared in, newest first by default, one row per video." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "_meta" - ] - }, - "EntityTopicListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the topic." - }, - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "id", - "name", - "count", - "sentiment" - ] - }, - "description": "Topics that co-occur with the entity in indexed media, highest count first by default." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "exportCapabilities", - "_meta" - ] - }, - "EntityPeopleListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the person." - }, - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "id", - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "People that co-occur with the entity in indexed media, highest count first by default." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "peopleMode": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Which co-occurrence the rows count. appearances: guest episodes. mentions: any shared media. Defaults to mentions for organizations and products, appearances otherwise." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "peopleMode", - "exportCapabilities", - "_meta" - ] - }, - "EntityOrganizationListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the organization." - }, - "name": { - "type": "string", - "description": "Organization name." - }, - "type": { - "type": "string", - "description": "Stored organization type: organization, company, or brand. Rows served from the stored top-N read organization." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Organization logo URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "id", - "name", - "type", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Organizations that co-occur with the entity in indexed media, highest count first by default." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "exportCapabilities", - "_meta" - ] - }, - "EntityProductListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the product." - }, - "name": { - "type": "string", - "description": "Product name." - }, - "type": { - "type": "string", - "description": "Stored entity type. Always product." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "avgSentiment": { - "type": [ - "number", - "null" - ], - "description": "Raw average sentiment score between -1 and 1. Null when no score was computed." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Product logo URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "id", - "name", - "type", - "count", - "avgSentiment", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Products related to the entity, highest count first by default. For an organization these are the products it owns that share media with it; for other types, products that co-occur in indexed media." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "exportCapabilities", - "_meta" - ] - }, - "EntityChannelListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the channel." - }, - "name": { - "type": "string", - "description": "Channel name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media on this channel counted for the entity: appearances for a person, mentions otherwise. Null when the plan hides counts." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Channel avatar URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "id", - "name", - "count", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Channels whose media carry the entity, highest count first by default." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "exportCapabilities", - "_meta" - ] - }, - "TopicPageResponse": { - "type": "object", - "properties": { - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Topic name." - }, - "type": { - "type": "string", - "enum": [ - "topic" - ], - "description": "Always topic." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Topic image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "isPriority": { - "type": "boolean", - "description": "True when the entity is flagged priority." - } - }, - "required": [ - "id", - "name", - "type", - "imageUrl", - "imageCheckedAt", - "isPriority" - ], - "description": "The topic." - }, - "stats": { - "type": "object", - "properties": { - "velocity": { - "type": [ - "integer", - "null" - ], - "description": "Mentions in the last 7 days. Null when the plan hides it." - }, - "velocity90d": { - "type": [ - "integer", - "null" - ], - "description": "Mentions in the last 90 days. Null when the plan hides it." - }, - "sentiment": { - "type": "number", - "description": "Reserved. Always 0." - }, - "reach": { - "type": "string", - "description": "Total views as display text, e.g. 1.2M." - }, - "reachRaw": { - "type": "number", - "description": "Total views of the media that mention the topic." - }, - "total": { - "type": "integer", - "description": "Total media that mention the topic." - }, - "latestMediaAt": { - "type": [ - "string", - "null" - ], - "description": "Newest publish date among the counted media. Null when none. The freshness gate does not withhold it." - }, - "people": { - "type": "integer", - "description": "Total people who appeared in media with the topic." - }, - "organizations": { - "type": "integer", - "description": "Total co-occurring organizations." - }, - "products": { - "type": "integer", - "description": "Total co-occurring products." - }, - "topics": { - "type": "integer", - "description": "Total related topics." - }, - "channels": { - "type": "integer", - "description": "Total channels that mention the topic." - } - }, - "required": [ - "velocity", - "velocity90d", - "sentiment", - "reach", - "reachRaw", - "total", - "latestMediaAt", - "people", - "organizations", - "products", - "topics", - "channels" - ], - "description": "Headline numbers and section totals for the topic." - }, - "mentionsByMonth": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] - }, - "description": "Media mentioning the topic per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." - }, - "relatedTopics": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "name", - "count", - "sentiment" - ] - }, - "description": "Co-occurring topics, highest count first." - }, - "voices": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "role": { - "type": "string", - "enum": [ - "Commentator" - ], - "description": "Always Commentator." - } - }, - "required": [ - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt", - "role" - ] - }, - "description": "People who appeared in media with the topic, highest count first." - }, - "companies": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Co-occurring organizations, highest count first." - }, - "products": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Co-occurring products, highest count first." - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Channel name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media on this channel that mention the entity. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt", - "slug" - ] - }, - "description": "Channels that mention the topic, highest count first." - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityPageMention" - }, - "description": "Newest media that mention the entity, one row per video, cut to the plan's media rows." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "entity", - "stats", - "mentionsByMonth", - "relatedTopics", - "voices", - "companies", - "products", - "channels", - "mentions", - "_meta" - ] - }, - "EntityPageMention": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Raw appearance row id (the first row for the video), as a string." - }, - "date": { - "type": "string", - "description": "Publish date as locale display text, or Unknown." - }, - "publishedAt": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." - }, - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title." - }, - "channel": { - "type": "string", - "description": "Source channel name, or Unknown Channel." - }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the source channel. Null when unknown." - }, - "channelHandle": { - "type": [ - "string", - "null" - ], - "description": "YouTube handle of the source channel. Null when unknown." - }, - "platform": { - "type": "string", - "enum": [ - "youtube" - ], - "description": "Always youtube." - }, - "thumbnail": { - "type": "string", - "description": "Video thumbnail URL." - }, - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL. Same value as thumbnail." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "duration": { - "type": [ - "number", - "null" - ], - "description": "Video length in seconds. Null when unknown." - }, - "type": { - "type": "string", - "enum": [ - "mention" - ], - "description": "Always mention." - }, - "context": { - "type": "string", - "description": "Description of the mention, or \"No description available\"." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). Absent when no mention in the video carries a score." - }, - "timestamp": { - "type": [ - "string", - "null" - ], - "description": "Earliest mention timestamp as MM:SS text. Null when the mention covers the full episode." - }, - "rawTimestamp": { - "type": [ - "string", - "null" - ], - "description": "Earliest mention timestamp as stored. Null when none." - }, - "mentionCount": { - "type": "integer", - "description": "Mentions of the entity in this video. At least 1." - }, - "allTimestamps": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Every non-zero mention timestamp in the video, as MM:SS text. Absent when there are none." - }, - "excerpt": { - "allOf": [ - { - "$ref": "#/components/schemas/PublishedExcerpt" - }, - { - "description": "A speakerless excerpt naming the entity in this media, when one is active." - } - ] - } - }, - "required": [ - "id", - "date", - "publishedAt", - "title", - "channel", - "channelId", - "channelHandle", - "platform", - "thumbnail", - "thumbnailUrl", - "videoId", - "duration", - "type", - "context", - "timestamp", - "rawTimestamp", - "mentionCount" - ] - }, - "OrganizationPageResponse": { - "type": "object", - "properties": { - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Organization name." - }, - "type": { - "type": "string", - "enum": [ - "organization" - ], - "description": "Always organization." - }, - "category": { - "type": "string", - "description": "Stored type: organization, company, or brand." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "isPriority": { - "type": "boolean", - "description": "True when the entity is flagged priority." - }, - "ownedChannels": { - "type": [ - "array", - "null" - ], - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Entity name." - }, - "display_name": { - "type": "string", - "description": "Label that tells a homonym apart, e.g. \"AdQuick (channel)\". Equals name when no label is needed." - }, - "type": { - "type": "string", - "description": "Entity type: channel or product." - }, - "route": { - "type": "string", - "description": "Site-relative route of the entity page on arcmira.com." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Entity image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "videoCount": { - "type": "integer", - "description": "Channels only: media rows published by the channel." - }, - "mentionCount": { - "type": "integer", - "description": "Products only: appearance rows of the product." - } - }, - "required": [ - "id", - "name", - "display_name", - "type", - "route", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Up to 10 channels this entity owns, most videos first. Null when it owns none." - }, - "ownedProducts": { - "type": [ - "array", - "null" - ], - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Entity name." - }, - "display_name": { - "type": "string", - "description": "Label that tells a homonym apart, e.g. \"AdQuick (channel)\". Equals name when no label is needed." - }, - "type": { - "type": "string", - "description": "Entity type: channel or product." - }, - "route": { - "type": "string", - "description": "Site-relative route of the entity page on arcmira.com." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Entity image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "videoCount": { - "type": "integer", - "description": "Channels only: media rows published by the channel." - }, - "mentionCount": { - "type": "integer", - "description": "Products only: appearance rows of the product." - } - }, - "required": [ - "id", - "name", - "display_name", - "type", - "route", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Up to 10 products this entity owns, most mentions first. Null when it owns none." - } - }, - "required": [ - "id", - "name", - "type", - "category", - "logoUrl", - "logoCheckedAt", - "isPriority", - "ownedChannels", - "ownedProducts" - ], - "description": "The organization and what it owns." - }, - "roleEdge": { - "type": [ - "object", - "null" - ], - "properties": { - "role": { - "type": "string", - "enum": [ - "ceo" - ], - "description": "Always ceo." - }, - "label": { - "type": "string", - "enum": [ - "CEO", - "CO-CEOS" - ], - "description": "CO-CEOS when operator-certified co-CEOs lead the organization, CEO otherwise." - }, - "name": { - "type": "string", - "description": "CEO name, or the co-CEO names joined with \"and\"." - }, - "href": { - "type": [ - "string", - "null" - ], - "description": "Site-relative link to the CEO page. Null for co-CEOs (see people) or when none." - }, - "receipt": { - "type": [ - "string", - "null" - ], - "description": "Display line naming the evidence for the primary CEO. Null when there is none to show." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Primary CEO image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked the primary CEO. Null until checked." - }, - "people": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Co-CEO name." - }, - "href": { - "type": [ - "string", - "null" - ], - "description": "Site-relative link to the co-CEO page. Null when none." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Co-CEO image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this person. Null until checked." - }, - "trueAppearances": { - "type": "integer", - "description": "Media the co-CEO appeared in." - }, - "receipt": { - "type": [ - "string", - "null" - ], - "description": "Display line naming the evidence. Null when there is none to show." - } - }, - "required": [ - "name", - "href", - "imageUrl", - "imageCheckedAt", - "trueAppearances", - "receipt" - ] - }, - "description": "Co-CEOs only: one entry per co-CEO. Absent for a single CEO." - }, - "confirmations": { - "type": "integer", - "description": "Most confirmations of the role across the CEO edges." - }, - "firstConfirmedAt": { - "type": [ - "string", - "null" - ], - "description": "When the primary CEO role was first confirmed. Null when unknown." - }, - "lastConfirmedAt": { - "type": [ - "string", - "null" - ], - "description": "When the primary CEO role was last confirmed. Null when unknown." - }, - "verifiedAt": { - "type": "string", - "description": "When the primary CEO role was verified." - }, - "trueAppearances": { - "type": "integer", - "description": "Most media any of the CEOs appeared in." - }, - "recentAppearances": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "Video title." - }, - "publishedAt": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." - }, - "videoId": { - "type": [ - "string", - "null" - ], - "description": "YouTube video id. Null when unknown." - }, - "channel": { - "type": [ - "string", - "null" - ], - "description": "Source channel name. Null when unknown." - }, - "channelHandle": { - "type": [ - "string", - "null" - ], - "description": "YouTube handle of the source channel. Null when unknown." - }, - "timestamp": { - "type": [ - "string", - "null" - ], - "description": "Appearance start as MM:SS text. Null when unknown." - } - }, - "required": [ - "title", - "publishedAt", - "videoId", - "channel", - "channelHandle", - "timestamp" - ] - }, - "description": "Newest media the CEO appeared in, outside the withheld window. Empty when no CEO has enough appearances to list." - } - }, - "required": [ - "role", - "label", - "name", - "href", - "receipt", - "imageUrl", - "imageCheckedAt", - "confirmations", - "firstConfirmedAt", - "lastConfirmedAt", - "verifiedAt", - "trueAppearances", - "recentAppearances" - ], - "description": "The verified CEO of the organization. Null when none is verified." - }, - "stats": { - "type": "object", - "properties": { - "velocity": { - "type": [ - "integer", - "null" - ], - "description": "Mentions in the last 90 days. Null when the plan hides it." - }, - "sentiment": { - "type": "number", - "description": "Reserved. Always 0." - }, - "reach": { - "type": "string", - "description": "Total views as display text, e.g. 1.2M." - }, - "reachRaw": { - "type": "number", - "description": "Total views of the media that mention the organization." - }, - "total": { - "type": "integer", - "description": "Total media that mention the organization." - }, - "latestMediaAt": { - "type": [ - "string", - "null" - ], - "description": "Newest publish date among the counted media. Null when none. The freshness gate does not withhold it." - }, - "people": { - "type": "integer", - "description": "Total co-occurring people." - }, - "topics": { - "type": "integer", - "description": "Total co-occurring topics." - }, - "products": { - "type": "integer", - "description": "Owned products with mentions." - }, - "channels": { - "type": "integer", - "description": "Total channels that mention the organization." - } - }, - "required": [ - "velocity", - "sentiment", - "reach", - "reachRaw", - "total", - "latestMediaAt", - "people", - "topics", - "products", - "channels" - ], - "description": "Headline numbers and section totals for the organization." - }, - "mentionsByMonth": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] - }, - "description": "Media mentioning the organization per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." - }, - "topics": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "name", - "count", - "sentiment" - ] - }, - "description": "Co-occurring topics, highest count first." - }, - "people": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Co-occurring people, highest count first." - }, - "products": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "neutral" - ], - "description": "Always neutral." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Products the organization owns that share media with it, most mentions first." - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Channel name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media on this channel that mention the entity. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt", - "slug" - ] - }, - "description": "Channels that mention the organization, highest count first." - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityPageMention" - }, - "description": "Newest media that mention the entity, one row per video, cut to the plan's media rows." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "entity", - "roleEdge", - "stats", - "mentionsByMonth", - "topics", - "people", - "products", - "channels", - "mentions", - "_meta" - ] - }, - "ProductPageResponse": { - "type": "object", - "properties": { - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Product name." - }, - "type": { - "type": "string", - "enum": [ - "product" - ], - "description": "Always product." - }, - "category": { - "type": "string", - "description": "The stored platform, or Software when none is stored." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "isPriority": { - "type": "boolean", - "description": "True when the entity is flagged priority." - }, - "owner": { - "type": [ - "object", - "null" - ], - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the owner." - }, - "name": { - "type": "string", - "description": "Owner name." - }, - "type": { - "type": "string", - "description": "Owner entity type: organization (legacy rows may read company or brand) or person." - }, - "route": { - "type": "string", - "description": "Site-relative route of the owner page on arcmira.com." - } - }, - "required": [ - "id", - "name", - "type", - "route" - ], - "description": "The organization or person that owns this entity. Null when no owner is recorded." - }, - "parentOrg": { - "type": [ - "object", - "null" - ], - "properties": { - "name": { - "type": "string", - "description": "Parent organization name." - }, - "slug": { - "type": "string", - "description": "The name lowercased with spaces as hyphens. Not guaranteed to match the organization page slug." - } - }, - "required": [ - "name", - "slug" - ], - "description": "Legacy: the organization recorded as owning the product in entity relations. Null when none. Prefer owner." - } - }, - "required": [ - "id", - "name", - "type", - "category", - "logoUrl", - "logoCheckedAt", - "isPriority", - "owner", - "parentOrg" - ], - "description": "The product and who owns it." - }, - "stats": { - "type": "object", - "properties": { - "velocity": { - "type": [ - "integer", - "null" - ], - "description": "Mentions in the last 90 days. Null when the plan hides it." - }, - "sentiment": { - "type": "number", - "description": "Reserved. Always 0." - }, - "reach": { - "type": "string", - "description": "Total views as display text, e.g. 1.2M." - }, - "reachRaw": { - "type": "number", - "description": "Total views of the media that mention the product." - }, - "total": { - "type": "integer", - "description": "Total media that mention the product." - }, - "latestMediaAt": { - "type": [ - "string", - "null" - ], - "description": "Newest publish date among the counted media. Null when none. The freshness gate does not withhold it." - }, - "people": { - "type": "integer", - "description": "Total co-occurring people." - }, - "topics": { - "type": "integer", - "description": "Total co-occurring topics." - }, - "organizations": { - "type": "integer", - "description": "Total co-occurring organizations." - }, - "channels": { - "type": "integer", - "description": "Total channels that mention the product." - } - }, - "required": [ - "velocity", - "sentiment", - "reach", - "reachRaw", - "total", - "latestMediaAt", - "people", - "topics", - "organizations", - "channels" - ], - "description": "Headline numbers and section totals for the product." - }, - "opportunities": { - "type": "object", - "properties": { - "evangelists": { - "type": "number", - "enum": [ - 0 - ], - "description": "Not computed. Always 0." - }, - "adInventory": { - "type": "number", - "enum": [ - 0 - ], - "description": "Not computed. Always 0." - } - }, - "required": [ - "evangelists", - "adInventory" - ], - "description": "Reserved for an advertiser view. Both counts are disabled and always 0; 0 means not computed, not a real count." - }, - "mentionsByMonth": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] - }, - "description": "Media mentioning the product per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." - }, - "topics": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "name", - "count", - "sentiment" - ] - }, - "description": "Up to 10 co-occurring topics, highest count first." - }, - "people": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Up to 10 co-occurring people, highest count first." - }, - "organizations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Up to 10 co-occurring organizations, highest count first." - }, - "channels": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Channel name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media on this channel that mention the entity. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt", - "slug" - ] - }, - "description": "Up to 6 channels that mention the product, highest count first." - }, - "mentions": { - "type": "array", - "items": { - "$ref": "#/components/schemas/EntityPageMention" - }, - "description": "Newest media that mention the entity, one row per video, cut to the plan's media rows." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "entity", - "stats", - "opportunities", - "mentionsByMonth", - "topics", - "people", - "organizations", - "channels", - "mentions", - "_meta" - ] - }, - "ChannelPageResponse": { - "type": "object", - "properties": { - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id." - }, - "name": { - "type": "string", - "description": "Channel name." - }, - "display_name": { - "type": "string", - "description": "Label that tells the channel apart from an owner of the same name, e.g. \"AdQuick (channel)\". Equals name otherwise." - }, - "type": { - "type": "string", - "enum": [ - "channel" - ], - "description": "Always channel." - }, - "category": { - "type": "string", - "description": "The stored platform, or YouTube when none is stored." - }, - "platform": { - "type": "string", - "description": "The stored platform, or youtube when none is stored." - }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id (UC form). Null when unknown." - }, - "youtubeChannelId": { - "type": [ - "string", - "null" - ], - "description": "Same value as channelId." - }, - "youtubeHandle": { - "type": [ - "string", - "null" - ], - "description": "YouTube @handle. Null when unknown." - }, - "url": { - "type": [ - "string", - "null" - ], - "description": "Channel URL. Null when unknown." - }, - "avatarUrl": { - "type": [ - "string", - "null" - ], - "description": "Channel avatar URL. Null when unknown." - }, - "avatarCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked the stored avatar. Null when the avatar came from channel metadata instead." - }, - "bannerUrl": { - "type": [ - "string", - "null" - ], - "description": "Channel banner URL. Null when unknown." - }, - "owner": { - "type": [ - "object", - "null" - ], - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the owner." - }, - "name": { - "type": "string", - "description": "Owner name." - }, - "type": { - "type": "string", - "description": "Owner entity type: organization (legacy rows may read company or brand) or person." - }, - "route": { - "type": "string", - "description": "Site-relative route of the owner page on arcmira.com." - } - }, - "required": [ - "id", - "name", - "type", - "route" - ], - "description": "The organization or person that owns this entity. Null when no owner is recorded." - } - }, - "required": [ - "id", - "name", - "display_name", - "type", - "category", - "platform", - "channelId", - "youtubeChannelId", - "youtubeHandle", - "url", - "avatarUrl", - "avatarCheckedAt", - "bannerUrl", - "owner" - ], - "description": "The channel and who owns it." - }, - "hosts": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Host names: configured hosts when the channel has them, else discovered ones." - }, - "stats": { - "type": "object", - "properties": { - "velocity": { - "type": "integer", - "description": "Videos published in the last 90 days." - }, - "sentiment": { - "type": "number", - "description": "Reserved. Always 0.5." - }, - "reach": { - "type": "string", - "description": "Average views per video as display text, e.g. 48.2K." - }, - "total": { - "type": "integer", - "description": "Indexed videos of the channel." - } - }, - "required": [ - "velocity", - "sentiment", - "reach", - "total" - ], - "description": "Header vitals. Open on every plan." - }, - "channelInfo": { - "type": "object", - "properties": { - "subscriberCount": { - "type": [ - "string", - "null" - ], - "description": "Subscriber count as YouTube displays it. Null when unknown." - }, - "avgViews": { - "type": [ - "string", - "null" - ], - "description": "Average views per video as display text. Null when the plan hides it." - }, - "episodeCount": { - "type": [ - "integer", - "null" - ], - "description": "Indexed videos. Null when the plan hides it." - }, - "guestCount": { - "type": [ - "integer", - "null" - ], - "description": "Unique guests. Null when the plan hides it." - }, - "startDate": { - "type": [ - "string", - "null" - ], - "description": "Publish date of the oldest indexed video. Null when none." - } - }, - "required": [ - "subscriberCount", - "avgViews", - "episodeCount", - "guestCount", - "startDate" - ], - "description": "Channel details, some gated by plan." - }, - "episodesByMonth": { - "type": "array", - "items": { - "type": "object", - "properties": { - "month": { - "type": "string", - "description": "Three-letter month name, e.g. Jan." - }, - "yearMonth": { - "type": "string", - "description": "The month as YYYY-MM." - }, - "count": { - "type": "integer", - "description": "Media that month. Months inside the withheld window read 0." - } - }, - "required": [ - "month", - "yearMonth", - "count" - ] - }, - "description": "Videos published per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart." - }, - "topics": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Topic name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Videos on the channel with the topic. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - } - }, - "required": [ - "name", - "count", - "sentiment" - ] - }, - "description": "Topics across the channel, highest count first." - }, - "hosts_detailed": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Host name." - }, - "role": { - "type": "string", - "description": "The recorded role cue, or Host." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Appearances by the host. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive" - ], - "description": "Always positive." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Host image URL. Null until resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this person. Null until checked." - } - }, - "required": [ - "name", - "role", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "Hosts with their details, in the same order as hosts." - }, - "guests": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Videos on the channel the guest appeared in. Null when the plan hides counts." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - }, - "role": { - "type": "string", - "enum": [ - "Guest" - ], - "description": "Always Guest." - } - }, - "required": [ - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt", - "role" - ] - }, - "description": "Guests who are not hosts, most appearances first." - }, - "organizations": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Organizations mentioned across the channel, most mentions first." - }, - "products": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Entity name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "logoUrl": { - "type": [ - "string", - "null" - ], - "description": "Logo URL. Null until an image has been resolved." - }, - "logoCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "name", - "count", - "sentiment", - "logoUrl", - "logoCheckedAt" - ] - }, - "description": "Products mentioned across the channel, most mentions first." - }, - "episodes": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Raw media row id, as a string." - }, - "date": { - "type": "string", - "description": "Publish date as locale display text, or Unknown." - }, - "publishedAt": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Null when unknown." - }, - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title." - }, - "channel": { - "type": "string", - "description": "The channel name." - }, - "channelId": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the video, else the channel's. Null when unknown." - }, - "channelHandle": { - "type": [ - "string", - "null" - ], - "description": "The channel's YouTube handle. Null when unknown." - }, - "platform": { - "type": "string", - "enum": [ - "youtube" - ], - "description": "Always youtube." - }, - "thumbnail": { - "type": "string", - "description": "Video thumbnail URL." - }, - "thumbnailUrl": { - "type": "string", - "description": "Video thumbnail URL. Same value as thumbnail." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "duration": { - "type": [ - "number", - "null" - ], - "description": "Video length in seconds. Null when unknown." - }, - "type": { - "type": "string", - "enum": [ - "interview", - "solo" - ], - "description": "interview when a guest appeared, solo otherwise." - }, - "context": { - "type": "string", - "description": "\"Interview with {guest}\" or \"Episode\"." - }, - "sentiment": { - "type": "string", - "enum": [ - "neutral" - ], - "description": "Always neutral." - }, - "timestamp": { - "type": "string", - "enum": [ - "00:00" - ], - "description": "Always 00:00." - }, - "guest": { - "type": [ - "string", - "null" - ], - "description": "One guest who appeared in the video. Null when none." - } - }, - "required": [ - "id", - "date", - "publishedAt", - "title", - "channel", - "channelId", - "channelHandle", - "platform", - "thumbnail", - "thumbnailUrl", - "videoId", - "duration", - "type", - "context", - "sentiment", - "timestamp", - "guest" - ] - }, - "description": "The channel's newest 50 indexed videos. Open on every plan." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - }, - "recommendations_summary": { - "type": "object", - "properties": { - "sponsor_count": { - "type": "integer", - "description": "Recurring sponsors of the channel. 0 when none." - }, - "top_sponsors": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Names of the top three sponsors. Present only on a Pro+ plan when the channel has sponsors." - } - }, - "required": [ - "sponsor_count" - ], - "description": "Sponsor teaser for the channel." - } - }, - "required": [ - "entity", - "hosts", - "stats", - "channelInfo", - "episodesByMonth", - "topics", - "hosts_detailed", - "guests", - "organizations", - "products", - "episodes", - "_meta", - "recommendations_summary" - ] - }, - "ChannelGuestListResponse": { - "type": "object", - "properties": { - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the person." - }, - "name": { - "type": "string", - "description": "Person name." - }, - "count": { - "type": [ - "integer", - "null" - ], - "description": "Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false)." - }, - "sentiment": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ], - "description": "Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score)." - }, - "imageUrl": { - "type": [ - "string", - "null" - ], - "description": "Person image URL. Null until an image has been resolved." - }, - "imageCheckedAt": { - "type": [ - "string", - "null" - ], - "description": "When the image pipeline last checked this entity. Null until checked." - } - }, - "required": [ - "id", - "name", - "count", - "sentiment", - "imageUrl", - "imageCheckedAt" - ] - }, - "description": "People who appeared on the channel, highest count first by default. count is the episodes they appeared in." - }, - "total": { - "type": "integer", - "description": "Rows matching the filter across all pages." - }, - "limit": { - "type": "integer", - "description": "Page size applied, after the plan clamp." - }, - "has_more": { - "type": "boolean", - "description": "True when more rows exist past this page." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - }, - "exportCapabilities": { - "type": "object", - "properties": { - "highestCount": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the highest-count ordering is always available." - }, - "lowestCount": { - "type": "boolean", - "description": "True when the lowest-count ordering is available." - }, - "alternateSort": { - "type": "boolean", - "description": "True when sort keys other than count are available." - }, - "search": { - "type": "boolean", - "description": "True when the q filter is available." - }, - "availableRows": { - "type": "integer", - "description": "Present only when a stored top-N is smaller than the full result set: the rows it holds." - } - }, - "required": [ - "highestCount", - "lowestCount", - "alternateSort", - "search" - ], - "description": "Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts." - }, - "_meta": { - "$ref": "#/components/schemas/ExposureMeta" - } - }, - "required": [ - "items", - "total", - "limit", - "has_more", - "next_cursor", - "exportCapabilities", - "_meta" - ] - }, - "MonitorListResponse": { - "type": "object", - "properties": { - "monitors": { - "type": "array", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/Monitor" - }, - { - "type": "object", - "properties": { - "trackerCount": { - "type": "integer", - "description": "Number of trackers in the monitor." - }, - "alertsThisMonth": { - "type": "integer", - "description": "Alert deliveries written for this monitor since the start of the calendar month." - }, - "slackIntegration": { - "type": [ - "object", - "null" - ], - "properties": { - "id": { - "type": "string", - "description": "Slack integration id." - }, - "team_name": { - "type": [ - "string", - "null" - ], - "description": "Slack workspace name." - }, - "channel_name": { - "type": [ - "string", - "null" - ], - "description": "Slack channel the integration posts to." - } - }, - "required": [ - "id", - "team_name", - "channel_name" - ], - "description": "Display metadata for the connected Slack integration. Null/absent when Slack is not configured." - } - }, - "required": [ - "trackerCount", - "alertsThisMonth" - ] - } - ] - }, - "description": "All monitors for the account, ordered by dashboard sort position, then name." - } - }, - "required": [ - "monitors" - ] - }, - "Monitor": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Monitor id." - }, - "name": { - "type": "string", - "description": "Monitor name." - }, - "isCollapsed": { - "type": "boolean", - "description": "True when the monitor is collapsed in the dashboard UI." - }, - "isPaused": { - "type": "boolean", - "description": "True when delivery is paused for all trackers in this monitor. New alerts are not queued, and queued email delivery checks the pause state again before sending. Use PATCH /v1/trackers/{id} with paused: true to pause one tracker." - }, - "sortOrder": { - "type": "integer", - "description": "Dashboard sort position." - }, - "notifyEmails": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Configured email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor." - }, - "emailRecipients": { - "type": "array", - "items": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "active", - "pending", - "unsubscribed", - "suppressed", - "removed", - "owner_unverified", - "plan_limited" - ] - }, - "invitationStatus": { - "type": "string", - "enum": [ - "sent", - "failed", - "limited", - "pending" - ] - } - }, - "required": [ - "email", - "status" - ] - }, - "description": "Recipient consent and invitation state. An account is not required to accept." - }, - "notifyFrequency": { - "type": "string", - "default": "realtime", - "description": "Delivery cadence. Values: realtime (deliver immediately), hourly (hourly digest), daily (daily digest). Free tier is limited to daily." - }, - "digestDay": { - "type": "string", - "default": "monday", - "description": "Day of week for digest delivery." - }, - "digestTime": { - "type": "string", - "default": "09:00", - "description": "Time of day (HH:MM) for digest delivery." - }, - "notifyWebhook": { - "type": "boolean", - "description": "True when webhook delivery is enabled." - }, - "webhookUrl": { - "type": [ - "string", - "null" - ], - "description": "Webhook destination URL. Null when no webhook is configured." - }, - "webhookSecretSet": { - "type": "boolean", - "description": "True when a webhook signing secret exists for this monitor. The secret itself is never returned on reads; enablement and rotation responses support recovery with the original Idempotency-Key during the valid recovery window." - }, - "webhookSecretHint": { - "type": [ - "string", - "null" - ], - "description": "Last 4 characters of the current signing secret, for identifying which secret you hold. Null until a secret exists." - }, - "webhookFailures": { - "type": "integer", - "default": 0, - "description": "Consecutive webhook delivery failures recorded for this monitor. Reset by a secret rotation or PATCHing notifyWebhook: true; 10 consecutive failures auto-disable a webhook. Note: the delivery pipeline currently accrues failures on the tracker that fired, so this monitor-level counter can lag." - }, - "webhookDisabledAt": { - "type": [ - "string", - "null" - ], - "description": "When the webhook was auto-disabled after repeated failures. Null while delivery is enabled. Re-enable by PATCHing notifyWebhook: true; rotation alone never re-enables." - }, - "webhookDisabledReason": { - "type": [ - "string", - "null" - ], - "description": "Why the webhook was auto-disabled. Null while delivery is enabled." - }, - "notifySlack": { - "type": "boolean", - "description": "True when Slack delivery is enabled." - }, - "slackIntegrationId": { - "type": [ - "string", - "null" - ], - "description": "Slack integration used for delivery. Null when Slack is not configured." - }, - "slackChannelId": { - "type": [ - "string", - "null" - ], - "description": "Slack channel to deliver to. Null when Slack is not configured." - }, - "createdAt": { - "type": "string", - "description": "When the monitor was created." - }, - "updatedAt": { - "type": "string", - "description": "When the monitor was last updated." - } - }, - "required": [ - "id", - "name", - "isCollapsed", - "isPaused", - "sortOrder", - "notifyEmails", - "notifyWebhook", - "webhookUrl", - "webhookSecretSet", - "webhookSecretHint", - "webhookDisabledAt", - "webhookDisabledReason", - "notifySlack", - "slackIntegrationId", - "slackChannelId", - "createdAt", - "updatedAt" - ] - }, - "MonitorMutationResponse": { - "type": "object", - "properties": { - "monitor": { - "allOf": [ - { - "$ref": "#/components/schemas/Monitor" - }, - { - "type": "object", - "properties": { - "trackerCount": { - "type": "integer", - "description": "Number of trackers in the monitor. Always 0 in the create response." - }, - "webhookSecret": { - "type": "string", - "description": "The webhook signing secret (\"whsec_...\"). Only present when this request NEWLY enabled webhook signing: a create with notifyWebhook: true and a webhookUrl, or a PATCH that turns the webhook on (or sets a URL) where no secret existed before. Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again." - } - }, - "required": [ - "trackerCount" - ] - } - ] - }, - "message": { - "type": "string", - "description": "Human-readable confirmation." - } - }, - "required": [ - "monitor", - "message" - ] - }, - "WebhookSecretRotateResponse": { - "type": "object", - "properties": { - "webhookSecret": { - "type": "string", - "description": "The NEW webhook signing secret (\"whsec_...\"). Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again." - }, - "webhookSecretHint": { - "type": "string", - "description": "Last 4 characters of the new secret, for identifying which secret you hold." - }, - "previousSecretExpiresAt": { - "type": [ - "string", - "null" - ], - "description": "End of the 24-hour overlap window. Until then, deliveries carry an additional X-Arcmira-Signature-Previous header computed with the previous secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. Null when the monitor had no previous secret (nothing to overlap)." - } - }, - "required": [ - "webhookSecret", - "webhookSecretHint", - "previousSecretExpiresAt" - ] - }, - "MonitorDeleteResponse": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Human-readable confirmation." - }, - "trackersDeleted": { - "type": "integer", - "description": "Number of trackers that were deleted along with the monitor." - } - }, - "required": [ - "message", - "trackersDeleted" - ] - }, - "MonitorTrackersResponse": { - "type": "object", - "properties": { - "trackers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Tracker id." - }, - "entityName": { - "type": "string", - "description": "Tracked entity name." - }, - "entityType": { - "type": "string", - "description": "Tracked entity type." - }, - "displayName": { - "type": "string", - "description": "User-facing display name. Falls back to entityName when not customized." - }, - "isPaused": { - "type": "boolean", - "description": "True when the tracker is paused." - }, - "pausedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker was paused. Null unless paused." - }, - "lastNotifiedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker last produced an alert. Null until the first alert." - }, - "createdAt": { - "type": "string", - "description": "When the tracker was created." - }, - "updatedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker was last updated." - }, - "monitorId": { - "type": "string", - "description": "The monitor id from the request path." - } - }, - "required": [ - "id", - "entityName", - "entityType", - "displayName", - "isPaused", - "pausedAt", - "lastNotifiedAt", - "createdAt", - "updatedAt", - "monitorId" - ] - }, - "description": "Trackers in the monitor, newest first." - }, - "count": { - "type": "integer", - "description": "Number of trackers returned." - } - }, - "required": [ - "trackers", - "count" - ] - }, - "AlertListResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Alert" - }, - "description": "Newest alerts first." - }, - "has_more": { - "type": "boolean", - "description": "True when older alerts exist past limit. The endpoint does not paginate: raise limit, up to 100, to read them." - }, - "next_cursor": { - "type": "null", - "description": "Always null: this endpoint does not paginate." - } - }, - "required": [ - "data", - "has_more", - "next_cursor" - ] - }, - "Alert": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Alert delivery id." - }, - "tracker_id": { - "type": [ - "string", - "null" - ], - "description": "Id of the tracker (tracked entity) that produced the alert." - }, - "monitor_id": { - "type": [ - "string", - "null" - ], - "description": "Id of the monitor the tracker belongs to. Null for trackers outside a monitor." - }, - "entity_id": { - "type": [ - "string", - "null" - ], - "description": "Public id (\"ent_{n}\") of the entity that triggered the alert, when recorded. Null on older rows that were written before entity_id was stored on alert_delivery. Resolve the entity through mention_id or the embedded tracker when this is null." - }, - "mention_id": { - "type": [ - "string", - "null" - ], - "description": "Public id (\"men_{n}\") of the mention/appearance row that triggered the alert. Joins directly against mention rows (e.g. /v1/mentions). Null when not appearance-scoped." - }, - "media_id": { - "type": [ - "integer", - "null" - ], - "description": "Media row that triggered the alert. A raw integer database id, matching the numeric media ids used elsewhere in the API (e.g. mention media.id). Null when not media-scoped." - }, - "appearance_id": { - "type": [ - "integer", - "null" - ], - "description": "Appearance row that triggered the alert. A raw integer database id, matching the numeric appearance_id on mention rows (same number as in mention_id). Null when not appearance-scoped." - }, - "excerpt_id": { - "type": [ - "string", - "null" - ], - "description": "Id of the active mention excerpt used as mention evidence. Null when the alert was sent before evidence was recorded." - }, - "evidence_kind": { - "type": [ - "string", - "null" - ], - "enum": [ - "excerpt", - null - ], - "description": "Which evidence layer was sent. Null on older rows." - }, - "channel": { - "type": "string", - "description": "Delivery channel. Values: email (sent by email), webhook (POSTed to the configured webhook URL), slack (sent to Slack)." - }, - "status": { - "type": "string", - "description": "Delivery status. Values: pending (queued for delivery), sent (delivered), failed (delivery failed; see error_message), skipped (delivery intentionally skipped)." - }, - "final_status": { - "type": [ - "string", - "null" - ], - "description": "Terminal status after retries. Null while delivery is still in progress." - }, - "error_message": { - "type": [ - "string", - "null" - ], - "description": "Error details for failed deliveries. Null unless delivery failed." - }, - "scheduled_at": { - "type": [ - "string", - "null" - ], - "description": "When delivery was scheduled. Null when delivered immediately." - }, - "sent_at": { - "type": [ - "string", - "null" - ], - "description": "When the alert was actually sent. Null until delivery succeeds." - }, - "created_at": { - "type": "string", - "description": "When the alert row was created." - }, - "tracker": { - "type": "object", - "properties": { - "id": { - "type": [ - "string", - "null" - ], - "description": "Tracker id." - }, - "entity_name": { - "type": [ - "string", - "null" - ], - "description": "Tracked entity name." - }, - "entity_type": { - "type": [ - "string", - "null" - ], - "description": "Tracked entity type." - }, - "display_name": { - "type": [ - "string", - "null" - ], - "description": "User-facing display name for the tracker. Null when not customized." - } - }, - "required": [ - "id", - "entity_name", - "entity_type", - "display_name" - ], - "description": "The tracker the alert belongs to. Fields are null when the tracker row was deleted." - }, - "monitor": { - "type": [ - "object", - "null" - ], - "properties": { - "id": { - "type": "string", - "description": "Monitor id." - }, - "name": { - "type": [ - "string", - "null" - ], - "description": "Monitor name." - } - }, - "required": [ - "id", - "name" - ], - "description": "The monitor the tracker belongs to. Null for trackers outside a monitor." - } - }, - "required": [ - "id", - "tracker_id", - "monitor_id", - "entity_id", - "mention_id", - "media_id", - "appearance_id", - "excerpt_id", - "evidence_kind", - "channel", - "status", - "final_status", - "error_message", - "scheduled_at", - "sent_at", - "created_at", - "tracker", - "monitor" - ] - }, - "MonitorAddTrackersResponse": { - "type": "object", - "properties": { - "attachedCount": { - "type": "integer", - "description": "Number of unique requested trackers attached." - }, - "message": { - "type": "string", - "description": "Human-readable confirmation, e.g. \"Added 3 tracker(s) to monitor\"." - }, - "monitorId": { - "type": "string", - "description": "The monitor id from the request path." - } - }, - "required": [ - "attachedCount", - "message", - "monitorId" - ] - }, - "TrackerListResponse": { - "type": "object", - "properties": { - "trackers": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Tracker" - }, - "description": "All trackers for the account, newest first." - }, - "count": { - "type": "integer", - "description": "Number of trackers returned." - } - }, - "required": [ - "trackers", - "count" - ] - }, - "Tracker": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Tracker id in the form \"trk_{hex}\"." - }, - "entityName": { - "type": "string", - "description": "The tracked entity name, as submitted." - }, - "entityType": { - "type": "string", - "description": "The tracked entity type. Values: person, organization, product, topic, channel." - }, - "displayName": { - "type": "string", - "description": "User-facing display name. Falls back to entityName when not customized." - }, - "notifyEmail": { - "type": "boolean", - "description": "True when this tracker delivers by email (default true at creation)." - }, - "notifyWebhook": { - "type": "boolean", - "description": "True when this tracker has a per-tracker webhook override enabled." - }, - "notifySlack": { - "type": "boolean", - "description": "True when this tracker has a per-tracker Slack override enabled." - }, - "webhookUrl": { - "type": [ - "string", - "null" - ], - "description": "Per-tracker webhook destination override. Null when the tracker uses its monitor's delivery settings." - }, - "slackChannelId": { - "type": [ - "string", - "null" - ], - "description": "Per-tracker Slack channel override. Null when not set." - }, - "slackIntegrationId": { - "type": [ - "string", - "null" - ], - "description": "Per-tracker Slack integration override. Null when not set." - }, - "filters": { - "type": [ - "object", - "null" - ], - "additionalProperties": {}, - "description": "Optional matching filters as submitted. Null when none were set." - }, - "isPaused": { - "type": "boolean", - "description": "True when the tracker is paused." - }, - "pausedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker was paused. Null unless paused." - }, - "lastNotifiedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker last produced an alert. Null until the first alert." - }, - "createdAt": { - "type": "string", - "description": "When the tracker was created." - }, - "updatedAt": { - "type": [ - "string", - "null" - ], - "description": "When the tracker was last updated." - }, - "monitorId": { - "type": "string", - "description": "The monitor this tracker belongs to. Absent for standalone trackers." - }, - "emailDeliveryCount": { - "type": "integer", - "description": "Email deliveries in the current billing period." - }, - "webhookDeliveryCount": { - "type": "integer", - "description": "Webhook deliveries in the current billing period." - }, - "slackDeliveryCount": { - "type": "integer", - "description": "Slack deliveries in the current billing period." - } - }, - "required": [ - "id", - "entityName", - "entityType", - "displayName", - "notifyEmail", - "notifyWebhook", - "notifySlack", - "webhookUrl", - "slackChannelId", - "slackIntegrationId", - "filters", - "isPaused", - "pausedAt", - "lastNotifiedAt", - "createdAt", - "updatedAt", - "emailDeliveryCount", - "webhookDeliveryCount", - "slackDeliveryCount" - ] - }, - "TrackerMutationResponse": { - "type": "object", - "properties": { - "tracker": { - "$ref": "#/components/schemas/Tracker" - }, - "message": { - "type": "string", - "description": "Human-readable confirmation." - } - }, - "required": [ - "tracker", - "message" - ] - }, - "MessageResponse": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Human-readable confirmation." - } - }, - "required": [ - "message" - ] - }, - "TeamMembersResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamMember" - }, - "description": "Active members, earliest join first. Removed members are excluded." - }, - "team": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Team id." - }, - "name": { - "type": "string", - "description": "Team name." - } - }, - "required": [ - "id", - "name" - ] - } - }, - "required": [ - "data", - "team" - ] - }, - "TeamMember": { - "type": "object", - "properties": { - "user_id": { - "type": "string", - "description": "Id of the member's user account." - }, - "name": { - "type": "string", - "description": "Member display name." - }, - "email": { - "type": "string", - "description": "Member email address." - }, - "role": { - "type": "string", - "enum": [ - "owner", - "admin", - "member", - "unpaid_admin" - ], - "description": "Team role. Values: owner, admin, member, unpaid_admin. Unpaid admins manage the team without a paid seat and have no product access." - }, - "seat_type": { - "type": "string", - "enum": [ - "standard", - "premium", - "free" - ], - "description": "Seat type. Values: standard (5,000 rows/month included), premium (25,000 rows/month included), free (the Unpaid Admin seat: no product access)." - }, - "joined_at": { - "type": [ - "string", - "null" - ], - "description": "When the member joined the team. Null when unknown." - } - }, - "required": [ - "user_id", - "name", - "email", - "role", - "seat_type", - "joined_at" - ] - }, - "TeamSpendResponse": { - "type": "object", - "properties": { - "period_start": { - "type": "string", - "description": "First day of the current period (YYYY-MM-DD)." - }, - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamMemberSpend" - }, - "description": "Per-member spend rows, earliest join first." - } - }, - "required": [ - "period_start", - "data" - ] - }, - "TeamMemberSpend": { - "type": "object", - "properties": { - "user_id": { - "type": "string", - "description": "Id of the member's user account." - }, - "name": { - "type": "string", - "description": "Member display name." - }, - "email": { - "type": "string", - "description": "Member email address." - }, - "role": { - "type": "string", - "enum": [ - "owner", - "admin", - "member", - "unpaid_admin" - ], - "description": "Team role. Values: owner, admin, member, unpaid_admin. Unpaid admins manage the team without a paid seat and have no product access." - }, - "seat_type": { - "type": "string", - "enum": [ - "standard", - "premium", - "free" - ], - "description": "Seat type. Values: standard (5,000 rows/month included), premium (25,000 rows/month included), free (the Unpaid Admin seat: no product access)." - }, - "rows_used": { - "type": "integer", - "description": "Rows this member consumed in the current period." - }, - "on_demand_spend_cents": { - "type": "integer", - "description": "Account-wide on-demand overage spend for this member in the current period, in US cents." - }, - "on_demand_enabled": { - "type": "boolean", - "description": "The member account's on-demand preference. Effective admission also depends on the team setting and remaining spend limit." - } - }, - "required": [ - "user_id", - "name", - "email", - "role", - "seat_type", - "rows_used", - "on_demand_spend_cents", - "on_demand_enabled" - ] - }, - "TeamUsageEventsResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TeamUsageEvent" - }, - "description": "Usage events across all team members, newest first." - }, - "has_more": { - "type": "boolean", - "description": "True when another page exists." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Opaque cursor for the next page. Null on the last page." - } - }, - "required": [ - "data", - "has_more", - "next_cursor" - ] - }, - "TeamUsageEvent": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Usage event id." - }, - "user_id": { - "type": [ - "string", - "null" - ], - "description": "Id of the member who generated the event." - }, - "action": { - "type": "string", - "description": "Usage action, e.g. entity_view, search, export." - }, - "entity_type": { - "type": [ - "string", - "null" - ], - "description": "Entity type the event touched, when applicable." - }, - "entity_name": { - "type": [ - "string", - "null" - ], - "description": "Entity name the event touched, when applicable." - }, - "total_rows": { - "type": "integer", - "description": "Total rows returned by the request." - }, - "premium_rows": { - "type": "integer", - "description": "Rows charged against the member's allowance (total minus free rows)." - }, - "request_path": { - "type": [ - "string", - "null" - ], - "description": "API path that generated the event." - }, - "is_overage": { - "type": "boolean", - "description": "True when this event billed on-demand rows past the included allowance." - }, - "created_at": { - "type": [ - "string", - "null" - ], - "description": "When the event was recorded." - } - }, - "required": [ - "id", - "user_id", - "action", - "entity_type", - "entity_name", - "total_rows", - "premium_rows", - "request_path", - "is_overage", - "created_at" - ] - }, - "TranscriptResponse": { - "type": "object", - "properties": { - "state": { - "type": "string", - "enum": [ - "ready" - ] - }, - "video": { - "$ref": "#/components/schemas/TranscriptVideo" - }, - "quality": { - "type": "string", - "enum": [ - "captions", - "premium" - ], - "description": "The requested quality. Premium is served only with an owned unlock; it never falls back to captions." - }, - "source": { - "type": "string", - "enum": [ - "creator_captions", - "third_party_quick", - "arcmira_premium" - ], - "description": "Public source class. creator_captions were written or approved by the channel, third_party_quick are YouTube automatic captions, arcmira_premium is our own diarized transcript." - }, - "language": { - "type": "string", - "description": "The resolved track code, asr-en style when the track is automatic." - }, - "languages": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaptionTrack" - }, - "description": "Every caption track the video offers. Empty when we did not list them on this call." - }, - "lines": { - "type": "array", - "items": { - "type": "object", - "properties": { - "start": { - "type": "number", - "description": "Line start in seconds from the beginning of the video." - }, - "end": { - "type": "number", - "description": "Line end in seconds." - }, - "text": { - "type": "string" - }, - "speaker": { - "type": "integer", - "description": "The person saying this line, present on every Premium line. Join it against speakers[].id." - }, - "index": { - "type": "integer", - "description": "Line index, present on every Premium line. Echo it as anchor.segmentIndex when you correct the line." - } - }, - "required": [ - "start", - "end", - "text" - ] - }, - "description": "Present when timestamps is true. Cite start with watch_url." - }, - "paragraphs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "start": { - "type": "number", - "description": "Paragraph start in seconds." - }, - "text": { - "type": "string" - }, - "speaker": { - "type": "integer" - } - }, - "required": [ - "start", - "text" - ] - }, - "description": "Present when timestamps is false. Lines joined on speaker changes for Premium and on sentence boundaries for captions." - }, - "speakers": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Speaker id, numbered in the order people first speak. Only meaningful with this read and its revision." - }, - "name": { - "type": "string", - "description": "The identified person, or Speaker 1, Speaker 2 and so on for a voice nobody has identified yet." - }, - "entity_id": { - "type": [ - "integer", - "null" - ], - "description": "Raw entity id of the identified person. Null when the speaker is unidentified." - }, - "confidence": { - "type": [ - "string", - "null" - ], - "description": "high when the name was reviewed, low when it is your own identification still awaiting review, null when nobody is identified." - } - }, - "required": [ - "id", - "name", - "entity_id", - "confidence" - ] - }, - "description": "Premium only. Speaker identification is right most of the time and wrong sometimes; say it came from Arcmira when a name matters." - }, - "revision": { - "type": "string", - "description": "Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. Echo it on every correction; a 409 means it changed underneath you, so read again." - }, - "range": { - "type": "object", - "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "required": [ - "start", - "end" - ], - "description": "Echoed when you sent start and end. Lines overlapping the window are returned. On captions only the window is billed; Premium retrieval is free." - }, - "rows_billed": { - "type": "integer", - "description": "Rows this call charged. 0 on a repeat of the same video, quality, language, and range inside the 7 day dedupe window, and always 0 on Premium retrieval." - }, - "as_of": { - "type": [ - "string", - "null" - ], - "description": "When the transcript was produced." - }, - "premium_job": { - "$ref": "#/components/schemas/TranscriptionJob" - }, - "access": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "invalid_request_error", - "authentication_error", - "permission_error", - "quota_exceeded", - "rate_limit_error", - "not_found", - "conflict_error", - "server_error" - ], - "description": "The error class. It fixes the HTTP status: invalid_request_error 400, authentication_error 401, quota_exceeded 402, permission_error 403, not_found 404, conflict_error 409, rate_limit_error 429, server_error 500. Switch on it for retry and gate handling." - }, - "code": { - "type": "string", - "description": "The specific condition, stable and snake_case; doc_url anchors on it. x-arcmira-codes on this schema lists every code with its type, gate and meaning. The list is open: new codes may appear inside an existing type, so switch on type and gate first." - }, - "reason": { - "type": "string", - "enum": [ - "no_credential", - "invalid", - "revoked" - ], - "description": "Only on invalid_api_key. no_credential: nothing was sent. invalid: a credential was sent and is unknown or malformed. revoked: the key exists and is no longer usable." - }, - "message": { - "type": "string", - "description": "One plain line. Names the fix or the unlock." - }, - "param": { - "type": "string", - "description": "The query or body parameter the gate refused, when one did." - }, - "gate": { - "type": "string", - "enum": [ - "rows", - "key", - "plan", - "freshness", - "exposure_law", - "rate", - "pagination" - ], - "description": "Which boundary refused. Present on every gate error; switch on it without parsing the message." - }, - "resource": { - "$ref": "#/components/schemas/ErrorResource" - }, - "unlock": { - "type": "object", - "properties": { - "tier": { - "type": "string", - "description": "The plan that lifts the gate." - }, - "url": { - "type": "string", - "description": "Absolute upgrade or sign-up URL carrying its ?src= attribution. Use it verbatim." - }, - "offer": { - "type": "null", - "description": "Reserved for the agent-discount offer. Always null today." - }, - "action": { - "type": "object", - "properties": { - "kind": { - "type": "string", - "description": "What the call does. send_signup_code sends a verification code to an address for an account key." - }, - "method": { - "type": "string", - "description": "HTTP method to use." - }, - "url": { - "type": "string", - "description": "Absolute endpoint carrying its ?src= attribution. Call it verbatim." - } - }, - "required": [ - "kind", - "method", - "url" - ], - "description": "The request that lifts the gate with no human, present when the caller can fix this itself. A key gate carries the signup send here; a plan or quota gate has no action and its url is a page a person opens." - } - }, - "required": [ - "tier", - "url", - "offer" - ], - "description": "How to lift the gate. Present when the gate has an unlock." - }, - "retry_after_seconds": { - "type": "integer", - "description": "Present on rate gates. Mirrors the Retry-After header." - }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, - "doc_url": { - "type": "string" - }, - "request_id": { - "type": "string" - } - }, - "required": [ - "type", - "code", - "message", - "doc_url", - "request_id" - ], - "description": "The gate that reduced this response. Present only when something was withheld; carries the same code, gate, and unlock an outright refusal would." - }, - "note": { - "type": "string", - "description": "One steering sentence for the agent reading this. On Premium it is the diarization disclosure verbatim." - } - }, - "required": [ - "state", - "video", - "quality", - "source", - "language", - "languages", - "rows_billed", - "as_of", - "note" - ], - "examples": [ - { - "video": { - "id": "dQw4w9WgXcQ", - "title": "TBPN | Tuesday, August 4", - "channel_id": "UC-DRzaGnL_vtBUpCFH5M0tg", - "channel_name": "TBPN", - "published_at": "2026-08-04T17:00:00.000Z", - "duration_seconds": 10800, - "watch_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" - }, - "quality": "captions", - "source": "creator_captions", - "language": "en", - "languages": [ - { - "code": "en", - "name": "English", - "generated": false - } - ], - "lines": [ - { - "start": 4787, - "end": 4791.5, - "text": "Ramp has been on the show for a while now." - }, - { - "start": 4791.5, - "end": 4796, - "text": "The pitch is still the same, spend less time on expenses." - } - ], - "rows_billed": 12, - "as_of": "2026-08-04T18:12:00.000Z", - "note": "This transcript is the video's own caption track. `creator_captions` were written or approved by the channel; `third_party_quick` are YouTube's automatic captions and can misspell names and drop punctuation. `language` says which track you got." - }, - { - "video": { - "id": "aB3dE5fG7hI", - "title": "Moment of Truth | The permitting fight nobody watched", - "channel_id": "UClWkDGXEzsh77GAhs90wpXw", - "channel_name": "Moment of Truth", - "published_at": "2026-08-19T14:00:00.000Z", - "duration_seconds": 4500, - "watch_url": "https://www.youtube.com/watch?v=aB3dE5fG7hI" - }, - "quality": "premium", - "source": "arcmira_premium", - "language": "en", - "languages": [ - { - "code": "en", - "name": "English", - "generated": false - } - ], - "lines": [ - { - "start": 612, - "end": 617.5, - "text": "The permit sat in review for nineteen months.", - "speaker": 0, - "index": 0 - }, - { - "start": 617.5, - "end": 623, - "text": "And the second review started before the first one closed.", - "speaker": 1, - "index": 1 - } - ], - "speakers": [ - { - "id": 0, - "name": "Dana Whitfield", - "entity_id": 4821, - "confidence": "high" - }, - { - "id": 1, - "name": "Speaker 1", - "entity_id": null, - "confidence": null - } - ], - "revision": "rev_7f2c1a.0k3m9x", - "rows_billed": 375, - "as_of": "2026-08-19T16:40:00.000Z", - "note": "Premium transcripts are Arcmira's own. Every Premium transcript is diarized: each line carries a speaker. We identify each speaker from the audio, the video, and internal and community review. We are right most of the time and wrong sometimes, so when a name matters, say it came from Arcmira's speaker identification and cite the line." - } - ] - }, - "TranscriptVideo": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "title": { - "type": "string", - "description": "Video title. Empty when we could not read it." - }, - "channel_id": { - "type": [ - "string", - "null" - ], - "description": "YouTube channel id of the source channel." - }, - "channel_name": { - "type": [ - "string", - "null" - ] - }, - "published_at": { - "type": [ - "string", - "null" - ], - "description": "Publish timestamp. Cite it as the date of anything you quote." - }, - "duration_seconds": { - "type": [ - "number", - "null" - ], - "description": "Video length in seconds. Null when unknown, which also means the row estimate was unknown." - }, - "watch_url": { - "type": "string", - "description": "Canonical YouTube watch URL." - } - }, - "required": [ - "id", - "title", - "channel_id", - "channel_name", - "published_at", - "duration_seconds", - "watch_url" - ] - }, - "CaptionTrack": { - "type": "object", - "properties": { - "code": { - "type": "string", - "description": "Caption track code, e.g. en or de. Pass it as language to select this track." - }, - "name": { - "type": "string", - "description": "Track name as YouTube reports it." - }, - "generated": { - "type": "boolean", - "description": "True for YouTube automatic captions, false for a track the channel wrote or approved." - } - }, - "required": [ - "code", - "name", - "generated" - ] - }, - "TranscriptionJob": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Transcription request id (UUID)." - }, - "video_id": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "state": { - "type": "string", - "enum": [ - "pending", - "ready", - "failed", - "refunded" - ], - "description": "Coarse outcome: pending until the Premium transcript is servable (ready), the purchase failed, or it was refunded." - }, - "status": { - "type": "string", - "enum": [ - "queued", - "downloading", - "transcribing", - "analyzing", - "complete", - "failed", - "refund_pending", - "refunded" - ], - "description": "Request status. Values: queued (accepted; audio download not started), downloading (fetching the video audio), transcribing (premium speech-to-text is running), analyzing (entity/commercial analysis is running), complete (premium transcript is servable via GET /v1/transcripts/{video_id}), failed (rejected intent or legacy purchase requiring accounting review), refund_pending (refund transaction must still complete), refunded (terminal failure; the charged rows were returned and the unlock this submission bought was revoked)." - }, - "stage": { - "type": [ - "string", - "null" - ], - "enum": [ - "queued", - "transcribing", - "analyzing", - null - ], - "description": "User-facing stage: downloading folds into transcribing. Values: queued (waiting to start), transcribing (downloading or transcribing), analyzing (analysis running). Null for terminal statuses and refund_pending." - }, - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "credits" - ] - }, - "amount": { - "type": "number", - "description": "Credits this purchase charged. 0 when a prior unlock made it free." - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the credits came from: the included allowance, on-demand usage, or both. Present once the purchase is funded." - } - }, - "required": [ - "unit", - "amount" - ], - "description": "What the purchase charged. Present on durable purchases; absent only on legacy requests." - }, - "eta_seconds": { - "type": "integer", - "description": "Estimated seconds until completion, re-derived from live pipeline telemetry on every poll. Only present while the request is in flight; absent on refund_pending, which has no completion ETA." - }, - "next_poll_seconds": { - "type": "integer", - "description": "Seconds to sleep before the next poll (also sent as the Retry-After header). Only present while the request is in flight." - }, - "error": { - "type": "string", - "description": "Failure reason. Only present when state is failed or refunded." - }, - "refunded": { - "type": "boolean", - "description": "True when the charge was returned. Only present when state is failed or refunded." - }, - "created_at": { - "type": "string", - "description": "When the request was submitted." - }, - "completed_at": { - "type": "string", - "description": "When the request reached a terminal status. Absent while in flight." - }, - "status_url": { - "type": "string", - "description": "Absolute URL of GET /v1/transcriptions/{id} for this job." - } - }, - "required": [ - "id", - "video_id", - "state", - "status", - "stage", - "created_at", - "status_url" - ], - "description": "Your open Premium purchase for this video, when captions were served while it prepares." - }, - "TranscriptPreparationRequired": { - "type": "object", - "properties": { - "state": { - "type": "string", - "enum": [ - "preparation_required" - ] - }, - "quality": { - "type": "string", - "enum": [ - "premium" - ] - }, - "video_id": { - "type": "string" - }, - "quote": { - "type": [ - "object", - "null" - ], - "properties": { - "rows": { - "type": "integer", - "description": "Whole-video row-equivalent price. 0 when a prior unlock makes it free." - }, - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "credits" - ] - }, - "amount": { - "type": "number", - "description": "Credits the purchase would charge." - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where those credits would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ] - }, - "eligible": { - "type": "boolean", - "description": "False when the account cannot buy right now; the POST then answers with the reason." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "On-demand money in whole cents the purchase would need. 0 means included credits cover it." - } - }, - "required": [ - "rows", - "charge", - "eligible", - "max_on_demand_cents" - ], - "description": "Null when the video has no known duration or is longer than 12 hours; the POST refuses it for the same reason." - }, - "action": { - "type": "object", - "properties": { - "method": { - "type": "string", - "enum": [ - "POST" - ] - }, - "url": { - "type": "string" - }, - "body": { - "type": "object", - "properties": { - "video_id": { - "type": "string" - } - }, - "required": [ - "video_id" - ] - } - }, - "required": [ - "method", - "url", - "body" - ], - "description": "The one request that prepares Premium from included credits and moves no money." - }, - "last_attempt": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "error": { - "type": "string" - } - }, - "required": [ - "status", - "error" - ], - "description": "The most recent failed or refunded purchase of this video, when there is one." - } - }, - "required": [ - "state", - "quality", - "video_id", - "quote", - "action" - ] - }, - "TranscriptPending": { - "type": "object", - "properties": { - "state": { - "type": "string", - "enum": [ - "pending" - ] - }, - "quality": { - "type": "string", - "enum": [ - "premium" - ] - }, - "video_id": { - "type": "string" - }, - "job": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptionJob" - }, - { - "description": "The open purchase. Poll job.status_url, or read this transcript again, after Retry-After." - } - ] - } - }, - "required": [ - "state", - "quality", - "video_id", - "job" - ] - }, - "TranscriptPurchaseQuote": { - "type": "object", - "properties": { - "video_id": { - "type": "string" - }, - "duration_seconds": { - "type": "number" - }, - "billing_scope": { - "type": "string", - "enum": [ - "full_video" - ] - }, - "owned": { - "type": "boolean" - }, - "eligible": { - "type": "boolean" - }, - "upgrade": { - "type": "object", - "properties": { - "label": { - "type": "string" - }, - "href": { - "type": "string" - } - }, - "required": [ - "label", - "href" - ], - "description": "Present when eligible is false: the plan checkout that can buy this transcript, as a button label and an absolute link." - }, - "quote": { - "$ref": "#/components/schemas/TranscriptQuote" - }, - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ] - }, - "credits_per_row": { - "type": "number" - }, - "max_on_demand_cents": { - "type": "number" - }, - "on_demand_cents_per_unit": { - "type": "number" - }, - "refund_policy": { - "type": "string" - } - }, - "required": [ - "video_id", - "duration_seconds", - "billing_scope", - "owned", - "eligible", - "quote", - "charge", - "credits_per_row", - "max_on_demand_cents", - "on_demand_cents_per_unit", - "refund_policy" - ] - }, - "VideoCaptionsResponse": { - "type": "object", - "properties": { - "video": { - "$ref": "#/components/schemas/TranscriptVideo" - }, - "languages": { - "type": "array", - "items": { - "$ref": "#/components/schemas/CaptionTrack" - }, - "description": "Every caption track the video offers. Pass a code, or a comma-separated priority list, as language on GET /v1/transcripts/{video_id}." - } - }, - "required": [ - "video", - "languages" - ], - "example": { - "video": { - "id": "kJQP7kiw5Fk", - "title": "Moment of Truth | The founder interview", - "channel_id": "UClWkDGXEzsh77GAhs90wpXw", - "channel_name": "Moment of Truth", - "published_at": "2026-08-21T16:00:00.000Z", - "duration_seconds": 3720, - "watch_url": "https://www.youtube.com/watch?v=kJQP7kiw5Fk" - }, - "languages": [ - { - "code": "en", - "name": "English", - "generated": false - }, - { - "code": "de", - "name": "German", - "generated": true - } - ] - } - }, - "TranscriptionSubmitResponse": { - "type": "object", - "properties": { - "job": { - "$ref": "#/components/schemas/TranscriptionJob" - }, - "existing": { - "type": "boolean", - "description": "True when a request for this video already existed (in flight or ready) and was returned instead of creating a new one." - } - }, - "required": [ - "job", - "existing" - ] - }, - "TranscriptionListResponse": { - "type": "object", - "properties": { - "requests": { - "type": "array", - "items": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptionJob" - }, - { - "type": "object", - "properties": { - "title": { - "type": [ - "string", - "null" - ], - "description": "Video title for display. Null when unknown." - } - }, - "required": [ - "title" - ] - } - ] - }, - "description": "Your requests in descending creation time and id order, up to the requested limit." - }, - "has_more": { - "type": "boolean", - "description": "True when another page exists in this traversal." - }, - "next_cursor": { - "type": [ - "string", - "null" - ], - "description": "Signed continuation for the same filter, limit and credential; null on the last page." - } - }, - "required": [ - "requests", - "has_more", - "next_cursor" - ] - }, - "CorrectionAcceptedResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true on acceptance." - }, - "kind": { - "type": "string", - "enum": [ - "line_edit", - "speaker_reassign", - "speaker_identify", - "add_person", - "entity_tag", - "segment_rewrite" - ], - "description": "The correction kind, echoed back." - }, - "result": { - "type": "object", - "additionalProperties": {}, - "description": "The stored pending-review row (kind-specific fields). Always carries the row id: use it with the matching withdrawal DELETE." - } - }, - "required": [ - "ok", - "kind", - "result" - ] - }, - "WithdrawnResponse": { - "type": "object", - "properties": { - "ok": { - "type": "boolean", - "enum": [ - true - ], - "description": "Always true: the pending row was withdrawn." - } - }, - "required": [ - "ok" - ] - }, - "TranscriptEditSubmittedResponse": { - "type": "object", - "properties": { - "edit": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/edits/{id}." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "segmentIndex": { - "type": "integer", - "description": "The line index the edit targets." - }, - "correctedText": { - "type": "string", - "description": "The corrected line text as stored, trimmed." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected", - "withdrawn" - ], - "description": "Review status. Always pending on submit." - } - }, - "required": [ - "id", - "videoId", - "segmentIndex", - "correctedText", - "status" - ], - "description": "The stored pending edit. It replaces any pending edit you had on the same line." - } - }, - "required": [ - "edit" - ] - }, - "SpeakerIdentificationSubmittedResponse": { - "type": "object", - "properties": { - "identification": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/speakers/{id}." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "speakerId": { - "type": "integer", - "description": "The speaker id you sent, from the transcript read's speakers[]." - }, - "entity": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Raw integer entity id of the person." - }, - "name": { - "type": "string", - "description": "Person name." - }, - "slug": { - "type": [ - "string", - "null" - ], - "description": "Person slug. Null when never slugged." - } - }, - "required": [ - "id", - "name", - "slug" - ], - "description": "The person the speaker now points at: the entity you named, an existing person matching the name, or a newly created one." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected", - "withdrawn", - "reverted" - ], - "description": "Review status. Always pending on submit." - } - }, - "required": [ - "id", - "videoId", - "speakerId", - "entity", - "status" - ], - "description": "The stored pending identification." - } - }, - "required": [ - "identification" - ] - }, - "VideoMergeSubmittedResponse": { - "type": "object", - "properties": { - "merge": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/merges/{id}." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "sourceName": { - "type": "string", - "description": "The name as it appears in the video." - }, - "targetEntityId": { - "type": "integer", - "description": "Raw integer entity id the name refers to." - }, - "replaceWith": { - "type": [ - "string", - "null" - ], - "description": "Respelling applied to the transcript text. Null when none." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected", - "reverted" - ], - "description": "Review status. Always pending on submit." - } - }, - "required": [ - "id", - "videoId", - "sourceName", - "targetEntityId", - "replaceWith", - "status" - ], - "description": "The stored pending merge." - } - }, - "required": [ - "merge" - ] - }, - "VideoMergeListResponse": { - "type": "object", - "properties": { - "merges": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "description": "Pending row id." - }, - "videoId": { - "type": "string", - "description": "YouTube video id (11 characters)." - }, - "sourceName": { - "type": "string", - "description": "The name as it appears in the video." - }, - "targetEntityId": { - "type": "integer", - "description": "Raw integer entity id the name refers to." - }, - "targetName": { - "type": "string", - "description": "Name of the target entity." - }, - "replaceWith": { - "type": [ - "string", - "null" - ], - "description": "Respelling applied to the transcript text. Null when none." - }, - "status": { - "type": "string", - "enum": [ - "pending", - "approved", - "rejected", - "reverted" - ], - "description": "Review status. Always pending: only pending rows are listed." - }, - "createdAt": { - "type": "string", - "description": "When the merge was submitted." - } - }, - "required": [ - "id", - "videoId", - "sourceName", - "targetEntityId", - "targetName", - "replaceWith", - "status", - "createdAt" - ] - }, - "description": "Your pending merges for the video, newest first." - } - }, - "required": [ - "merges" - ] - } - }, - "parameters": {} - }, - "paths": { - "/v1/health": { - "get": { - "tags": [ - "Meta" - ], - "operationId": "get_health", - "summary": "Health check", - "security": [], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HealthResponse" - } - } - } - }, - "429": { - "description": "Rate limit exceeded. Retry-After carries the wait.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/openapi.json": { - "get": { - "tags": [ - "Meta" - ], - "operationId": "get_openapi_document", - "summary": "OpenAPI document", - "security": [], - "responses": { - "200": { - "description": "This OpenAPI 3.1 document.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OpenApiDocument" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "description": "Rate limit exceeded. Retry-After carries the wait.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/me": { - "get": { - "tags": [ - "Meta" - ], - "operationId": "get_me", - "summary": "Current API key context", - "description": "Available even when the account has exhausted its usage allowance. Returns the credential making the request (key_id, key_label, credential_kind), the masked account email, the tier, scopes, rate limit, row usage with period_resets_at, and account settings. settings.transcripts is what a transcript request that names no parameter of its own receives: every key of the account resolves against it.", - "security": [ - { - "bearerAuth": [] - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MeResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/me/settings": { - "patch": { - "tags": [ - "Meta" - ], - "operationId": "update_my_settings", - "summary": "Set account defaults", - "description": "Sets the account defaults every key of the account resolves against. Send only the fields you are changing; an omitted field keeps the value it has. Resolution order for every transcript request is the explicit parameter, then these settings, then the platform default, so a default never overrides a parameter the caller sent. Defaults live on the account, never on a key: two keys of one account answer the same request the same way. The response echoes the resolved settings.", - "security": [ - { - "bearerAuth": [] - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "transcripts": { - "type": "object", - "properties": { - "quality": { - "type": "string", - "enum": [ - "captions", - "premium" - ], - "description": "Default transcript quality for this account: captions or premium. premium reads require an existing purchase. Owned transcripts remain readable after a plan downgrade; new purchases require an eligible plan." - }, - "language": { - "type": "string", - "description": "Default comma-separated caption language priority list, tried in order (e.g. \"de,en\"), at most 5 codes. Use asr for the first automatic track and asr- for a specific one." - }, - "timestamps": { - "type": "boolean", - "description": "Default for the timestamps parameter. false makes paragraphs[] the default body shape instead of lines[]." - } - }, - "description": "Fields to change. An omitted field keeps the value the account already carries." - } - }, - "required": [ - "transcripts" - ] - } - } - } - }, - "responses": { - "200": { - "description": "Settings updated", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MeSettingsResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/signups": { - "post": { - "tags": [ - "Meta" - ], - "operationId": "create_signup", - "summary": "Send a verification code to an address", - "description": "Starts the signup that ends in an account key, with no key and no login. Sends a 6 digit code to the address, valid for 10 minutes and 5 attempts, and answers 202 with the verify call. Sends are capped at 3 per address per hour, 10 per IP per hour, and 25 per client fingerprint per day; past a cap the response is 429 signup_send_limited with retry_after_seconds and an unlock whose action is this call. An address that already has an account gets a code too; verifying it mints a key on that account. Send { \"email\": \"agent@example.com\" }, with an optional \"src\" naming the surface that sent you.", - "security": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "maxLength": 254, - "description": "The address the verification code is sent to. Case is folded; the same address in any casing is one account and one send budget." - }, - "src": { - "type": "string", - "maxLength": 32, - "description": "The agent surface that sent you, as a value from the ?src= registry. Also accepted as ?src= on the URL. The unlock in a refusal carries it back." - } - }, - "required": [ - "email" - ] - } - } - } - }, - "responses": { - "202": { - "description": "Code sent", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SignupSentResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "description": "Rate limit exceeded. Retry-After carries the wait.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "500": { - "$ref": "#/components/responses/ServerError" - }, - "503": { - "description": "server_error. The verification email could not be sent. Retry-After carries the wait.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/v1/signups/verify": { - "post": { - "tags": [ - "Meta" - ], - "operationId": "verify_signup", - "summary": "Exchange a verification code for an account key", - "description": "Consumes the code POST /v1/signups sent, creates the account when the address has none, and mints an arc_sk_ key on it: the read scope, the free tier's lifetime row pool, no expiry. A wrong, expired, or spent code is 400 signup_code_invalid on param code, naming the attempts left; its unlock action is a new send. Send { \"email\": \"agent@example.com\", \"code\": \"482913\" }.", - "security": [], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "email": { - "type": "string", - "maxLength": 254, - "description": "The address the code was sent to." - }, - "code": { - "type": "string", - "pattern": "^\\d+$", - "description": "The six digit code from the email. Ten minutes, five attempts, then a new send is required." - } - }, - "required": [ - "email", - "code" - ] - } - } - } - }, - "responses": { - "201": { - "description": "Account key minted", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SignupVerifiedResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "description": "Rate limit exceeded. Retry-After carries the wait.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/search": { - "get": { - "tags": [ - "Search" - ], - "operationId": "search", - "summary": "Resolve a name to a single entity", - "description": "Single-result name resolver: exact, case-insensitive match with curated alias support. Returns at most one entity and does not paginate (single page; there is no cursor). When `type` is passed and the name resolves to an entity of a different type, the response is `{ found: false }`. For fuzzy multi-result discovery use /v1/entities/search instead.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Entity name to resolve. Exact, case-insensitive match; curated merge-rule aliases (e.g. \"Ford\" resolving to Ford Motor Company) are honored." - }, - "required": true, - "description": "Entity name to resolve. Exact, case-insensitive match; curated merge-rule aliases (e.g. \"Ford\" resolving to Ford Motor Company) are honored.", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ], - "description": "Restrict the match to one entity type. When the name resolves to an entity of a different type, the response is { found: false }. organization also matches legacy company/brand rows." - }, - "required": false, - "description": "Restrict the match to one entity type. When the name resolves to an entity of a different type, the response is { found: false }. organization also matches legacy company/brand rows.", - "name": "type", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SearchResolveResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/search": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "search_entities", - "summary": "Fuzzy entity discovery", - "description": "Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 2 - }, - "required": true, - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ] - }, - "required": false, - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "has_recommendations_data", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 25, - "default": 10 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntitySearchResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/resolve": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "resolve_entity", - "summary": "A name to one id, before any filter", - "description": "Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name (\"the startup bank\", \"on My First Million\"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 2, - "description": "A name, @handle, YouTube URL or channel id (UC...). One thing per call." - }, - "required": true, - "description": "A name, @handle, YouTube URL or channel id (UC...). One thing per call.", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ], - "description": "Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id." - }, - "required": false, - "description": "Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id.", - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 15, - "default": 8, - "description": "Candidates to return, 1 to 15. Default 8." - }, - "required": false, - "description": "Candidates to return, 1 to 15. Default 8.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "minLength": 2, - "maxLength": 300, - "description": "What the user said about the name, in their words (\"the startup bank\", \"Canada's prime minister\", \"on My First Million\"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context." - }, - "required": false, - "description": "What the user said about the name, in their words (\"the startup bank\", \"Canada's prime minister\", \"on My First Million\"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context.", - "name": "context", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityResolveResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/lookup": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "lookup_entity", - "summary": "Resolve an entity to its canonical stable ID", - "description": "Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": false, - "name": "id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "name", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ] - }, - "required": false, - "name": "type", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityLookupResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/cards": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "get_entity_cards", - "summary": "Batch compact entity cards by id", - "description": "Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Comma-separated raw integer entity ids, 1 to 50 of them (e.g. \"12,844,1032\"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response." - }, - "required": true, - "description": "Comma-separated raw integer entity ids, 1 to 50 of them (e.g. \"12,844,1032\"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response.", - "name": "ids", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityCardsResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/{id}": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "get_entity", - "summary": "Get canonical entity metadata", - "description": "Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." - }, - "required": true, - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", - "name": "id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityDetailResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/{id}/mentions": { - "get": { - "tags": [ - "Mentions" - ], - "operationId": "list_entity_mentions", - "summary": "List mentions for an entity", - "description": "Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." - }, - "required": true, - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_name", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ] - }, - "required": false, - "name": "sentiment", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "is_appearance", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_from", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_to", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "full" - ] - }, - "required": false, - "name": "details", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MentionListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/entities/{id}/recommendations": { - "get": { - "tags": [ - "Recommendations" - ], - "operationId": "list_entity_recommendations", - "summary": "List recommendations for an entity", - "description": "Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." - }, - "required": true, - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_name", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "ad_read", - "endorsement", - "mention", - "all" - ], - "default": "all" - }, - "required": false, - "name": "mention_class", - "in": "query" - }, - { - "schema": { - "type": [ - "number", - "null" - ], - "minimum": 0, - "maximum": 1, - "default": 0.7 - }, - "required": false, - "name": "min_confidence", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_from", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_to", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "include_disputed", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecommendationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/mentions": { - "get": { - "tags": [ - "Mentions" - ], - "operationId": "list_mentions", - "summary": "Search mentions across media", - "description": "Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "entity_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "entity_name", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ] - }, - "required": false, - "name": "entity_type", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_name", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "positive", - "neutral", - "negative" - ] - }, - "required": false, - "name": "sentiment", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "is_appearance", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_from", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_to", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "full" - ] - }, - "required": false, - "name": "details", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MentionListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/recommendations": { - "get": { - "tags": [ - "Recommendations" - ], - "operationId": "list_recommendations", - "summary": "Search recommendations across media", - "description": "Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "entity_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "entity_name", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "person", - "organization", - "product", - "topic", - "channel" - ] - }, - "required": false, - "name": "entity_type", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_id", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "channel_name", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "ad_read", - "endorsement", - "mention", - "all" - ], - "default": "all" - }, - "required": false, - "name": "mention_class", - "in": "query" - }, - { - "schema": { - "type": [ - "number", - "null" - ], - "minimum": 0, - "maximum": 1, - "default": 0.7 - }, - "required": false, - "name": "min_confidence", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_from", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "date_to", - "in": "query" - }, - { - "schema": { - "type": "boolean" - }, - "required": false, - "name": "include_disputed", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RecommendationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/feedback": { - "post": { - "tags": [ - "Feedback" - ], - "operationId": "submit_feedback", - "summary": "Submit feedback and corrections for a prior API query", - "description": "Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status \"logged\"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "enum": [ - "recommendations", - "channel_sponsors", - "mentions", - "entities_search", - "entities", - "channels", - "monitor_alert", - "appearances", - "search" - ], - "description": "Feedback type when omitted from the JSON body. Provide type in either location; the body takes precedence." - }, - "required": false, - "description": "Feedback type when omitted from the JSON body. Provide type in either location; the body takes precedence.", - "name": "type", - "in": "query" - }, - { - "schema": { - "type": "string", - "minLength": 1, - "description": "Query being reviewed when omitted from the JSON body. Body query fields override matching query-string fields." - }, - "required": false, - "description": "Query being reviewed when omitted from the JSON body. Body query fields override matching query-string fields.", - "name": "query", - "in": "query" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": [ - "recommendations", - "channel_sponsors", - "mentions", - "entities_search", - "entities", - "channels", - "monitor_alert", - "appearances", - "search" - ], - "description": "The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search)." - }, - "query": { - "type": "object", - "additionalProperties": {}, - "description": "The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id." - }, - "endpoint": { - "type": "string", - "maxLength": 500 - }, - "method": { - "type": "string", - "enum": [ - "GET", - "POST", - "PATCH", - "PUT", - "DELETE" - ] - }, - "request_id": { - "type": "string", - "maxLength": 200 - }, - "result_url": { - "type": "string", - "maxLength": 2000 - }, - "source_url": { - "type": "string", - "maxLength": 2000 - }, - "notes": { - "type": "string", - "maxLength": 4000 - }, - "corrections": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "minLength": 1, - "description": "Public id of the row being corrected, from the response you received: men_* (mentions, appearances), com_* (recommendations), ent_* (entities, sponsors), or the alert row id (monitor_alert). Omit for missed_alert and missing_result corrections, which have no row to target." - }, - "mention_class": { - "type": "string", - "enum": [ - "ad_read", - "endorsement", - "mention" - ] - }, - "reason": { - "type": "string", - "enum": [ - "false_positive_ad_read", - "false_positive_endorsement", - "missed_ad_read", - "missed_endorsement", - "wrong_classification", - "wrong_entity", - "other" - ] - }, - "issue_type": { - "type": "string", - "enum": [ - "wrong_entity_type", - "wrong_entity", - "duplicate_entity", - "merge_suggestion", - "missing_result", - "stale_metadata", - "wrong_classification", - "bad_ranking", - "false_positive_alert", - "wrong_media", - "wrong_timestamp", - "duplicate_alert", - "missed_alert", - "delivery_issue", - "person_not_present", - "wrong_person", - "wrong_appearance_role", - "other" - ], - "description": "Issue classification for the correction. Entity-family values: wrong_entity_type (right entity, wrong type), wrong_entity (the row points at the wrong canonical entity), duplicate_entity (results split across variants of the same entity), merge_suggestion (propose the canonical merge for split variants), missing_result (a result you know should exist is absent), stale_metadata (name/website/channel metadata is outdated), wrong_classification (class-level error on a commercial row), bad_ranking (duplicates or aliases ranking above the canonical entity). monitor_alert values: false_positive_alert (the alert should not have fired), wrong_media (fired against the wrong video), wrong_timestamp (fired at the wrong position in the video), duplicate_alert (the same occurrence fired more than once), missed_alert (an expectation: an alert that should have fired but did not; no row to target), delivery_issue (the delivery itself was wrong: wrong channel, not received). appearances values: person_not_present (the person does not appear in the media), wrong_person (the appearance is attributed to the wrong person), wrong_appearance_role (right person, wrong role, e.g. guest vs host). other (escape hatch; detail in notes)." - }, - "suggested_change": { - "anyOf": [ - { - "$ref": "#/components/schemas/MergeSuggestionChange" - }, - { - "$ref": "#/components/schemas/WrongEntityChange" - }, - { - "$ref": "#/components/schemas/WrongEntityTypeChange" - }, - { - "$ref": "#/components/schemas/MissingResultChange" - }, - { - "$ref": "#/components/schemas/WrongClassificationChange" - }, - { - "$ref": "#/components/schemas/StaleMetadataChange" - }, - { - "$ref": "#/components/schemas/BadRankingChange" - }, - { - "$ref": "#/components/schemas/MissedAlertChange" - }, - { - "$ref": "#/components/schemas/DeliveryIssueChange" - }, - { - "$ref": "#/components/schemas/FreeformSuggestedChange" - } - ], - "description": "Your concrete proposed fix, shaped by issue_type: merge_suggestion → MergeSuggestionChange, wrong_entity/wrong_person → WrongEntityChange, wrong_entity_type → WrongEntityTypeChange, missing_result → MissingResultChange, wrong_classification → WrongClassificationChange, stale_metadata → StaleMetadataChange, bad_ranking → BadRankingChange, missed_alert → MissedAlertChange, delivery_issue → DeliveryIssueChange. Unknown keys are accepted and logged verbatim; only non-object values are rejected. Omit suggested_change entirely when you do not have a concrete fix." - }, - "notes": { - "type": "string", - "maxLength": 2000 - } - } - }, - "maxItems": 100 - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FeedbackResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/feedback/{feedback_id}": { - "get": { - "tags": [ - "Feedback" - ], - "operationId": "get_feedback", - "summary": "Read back a feedback submission and its review status", - "description": "Returns the submission (id, type, query, notes, created_at) plus its per-correction rows, each with a review status in the public vocabulary: pending_review, needs_information, accepted, accepted_with_changes, rejected, withdrawn, applied, reverted (accepted means a reviewer agreed; applied means the change is live in the index). Only the submitting user's keys can read a submission; unknown ids and other users' submissions both return 404 (never 403).", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The feedback submission id POST /v1/feedback returned." - }, - "required": true, - "description": "The feedback submission id POST /v1/feedback returned.", - "name": "feedback_id", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FeedbackReadbackResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{channel_id}/sponsors": { - "get": { - "tags": [ - "Recommendations" - ], - "operationId": "list_channel_sponsors", - "summary": "List recurring channel sponsors", - "description": "Rollup of recurring sponsors for a YouTube channel, ordered by ad read count. On a Pro+ plan the full list is served and min_ad_reads (default 3), status, and limit apply. Every other plan receives the free slice an anonymous visitor sees on arcmira.com, with meta.total naming the true count and access naming the gate; passing min_ad_reads, status, or limit on such a plan is refused with filter_requires_paid. Pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "YouTube channel id, the UC... form." - }, - "required": true, - "description": "YouTube channel id, the UC... form.", - "name": "channel_id", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "description": "Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid." - }, - "required": false, - "description": "Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid.", - "name": "min_ad_reads", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "active", - "lapsed", - "ended", - "uncertain" - ], - "description": "Filter against the curated known-advertisers dataset. Pro+ only." - }, - "required": false, - "description": "Filter against the curated known-advertisers dataset. Pro+ only.", - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 200, - "description": "Sponsors to return. Default 100. Pro+ only; other plans receive the free slice." - }, - "required": false, - "description": "Sponsors to return. Default 100. Pro+ only; other plans receive the free slice.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChannelSponsorsResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/transcripts/search": { - "get": { - "tags": [ - "Search" - ], - "operationId": "search_transcripts", - "summary": "Retrieve spoken transcript slices for one topic", - "description": "Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 2, - "description": "One topic or phrase. Do not concatenate unrelated names; make one call per topic." - }, - "required": true, - "description": "One topic or phrase. Do not concatenate unrelated names; make one call per topic.", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." - }, - "required": false, - "description": "Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", - "name": "channel_ids", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Alias of channel_ids for code-mode clients; the union of both is the scope." - }, - "required": false, - "description": "Alias of channel_ids for code-mode clients; the union of both is the scope.", - "name": "channel", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." - }, - "required": false, - "description": "Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", - "name": "entity_ids", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." - }, - "required": false, - "description": "Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", - "name": "about", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." - }, - "required": false, - "description": "Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", - "name": "by", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk." - }, - "required": false, - "description": "Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk.", - "name": "kind", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened." - }, - "required": false, - "description": "ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened.", - "name": "published_after", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Only media published before this day." - }, - "required": false, - "description": "ISO date. Only media published before this day.", - "name": "published_before", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "arcmira_premium", - "creator_captions", - "third_party_quick" - ], - "description": "Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid." - }, - "required": false, - "description": "Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid.", - "name": "source", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 20, - "default": 5, - "description": "Chunks to return, 1 to 20. Default 5." - }, - "required": false, - "description": "Chunks to return, 1 to 20. Default 5.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TranscriptSearchResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - }, - "503": { - "description": "search_unavailable. Search is unavailable right now. Retry-After carries the wait; nothing was charged.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - } - } - } - }, - "/v1/entities/{id}/momentum": { - "get": { - "tags": [ - "Entities" - ], - "operationId": "get_entity_momentum", - "summary": "Spoken-web heat for one entity", - "description": "Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." - }, - "required": true, - "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityMomentumResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{channel_id}/coverage": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "get_channel_coverage", - "summary": "What the index holds for a channel", - "description": "How many videos of a YouTube channel are searchable, the newest publish date among them, and the split by transcript source class. Call it when a search or mention lookup came back empty, before telling anyone we do not cover a show, and cite indexed_through as the as-of date for mentions and search_indexed_through for transcript search. It cannot request indexing; channel backfill is not available yet. Free (0 rows). Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "YouTube channel id, the UC... form." - }, - "required": true, - "description": "YouTube channel id, the UC... form.", - "name": "channel_id", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChannelCoverageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{channel_id}/videos": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_videos", - "summary": "Newest indexed videos of a channel", - "description": "The indexed videos of a YouTube channel, newest first, each with its video_id, title, publish date, duration, view count, and watch_url on arcmira.com. Pass next_cursor as cursor to continue. The signed token binds the route, filters, caller and visibility; invalid or old tokens return invalid_cursor. A first-page media ID fence excludes later insertions, including old-date backfills; edits and deletions to existing rows remain live. Call it for the latest or most recent episode of a show, or to list what a show published in a window, then pass a video_id to GET /v1/mentions/counts video_ids for what that episode mentions or to GET /v1/transcripts/{video_id} to read it. indexed_through is the newest date we hold for the channel. An empty list means nothing is indexed; channel backfill is not available yet. Bills one row per video returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "YouTube channel id, the UC... form." - }, - "required": true, - "description": "YouTube channel id, the UC... form.", - "name": "channel_id", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 25, - "default": 10, - "description": "Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode." - }, - "required": false, - "description": "Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor." - }, - "required": false, - "description": "Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Only videos published on or after this day." - }, - "required": false, - "description": "ISO date. Only videos published on or after this day.", - "name": "published_after", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Only videos published before this day." - }, - "required": false, - "description": "ISO date. Only videos published before this day.", - "name": "published_before", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChannelVideosResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/mentions/counts": { - "get": { - "tags": [ - "Mentions" - ], - "operationId": "count_mentions", - "summary": "Ranked catalog counts of who a set of channels mention", - "description": "A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them." - }, - "required": false, - "description": "Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them.", - "name": "channel_ids", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities." - }, - "required": false, - "description": "Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities.", - "name": "entity_ids", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos." - }, - "required": false, - "description": "Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos.", - "name": "video_ids", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate." - }, - "required": false, - "description": "Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate.", - "name": "entity_types", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mentions", - "appearances", - "both" - ], - "default": "mentions", - "description": "mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions." - }, - "required": false, - "description": "mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened." - }, - "required": false, - "description": "ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened.", - "name": "published_after", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "ISO date. Only media published before this day." - }, - "required": false, - "description": "ISO date. Only media published before this day.", - "name": "published_before", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 40, - "default": 20, - "description": "Rows in the ranked table, 1 to 40. Default 20." - }, - "required": false, - "description": "Rows in the ranked table, 1 to 40. Default 20.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MentionCountsResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}": { - "get": { - "tags": [ - "People" - ], - "operationId": "get_person", - "summary": "Get a person entity page", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PersonPageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/appearances": { - "get": { - "tags": [ - "Appearances" - ], - "operationId": "list_person_appearances", - "summary": "List person appearances", - "description": "Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PersonAppearanceListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/topics": { - "get": { - "tags": [ - "People" - ], - "operationId": "list_person_topics", - "summary": "List topics related to a person", - "description": "The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityTopicListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/people": { - "get": { - "tags": [ - "People" - ], - "operationId": "list_person_people", - "summary": "List people related to a person", - "description": "The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityPeopleListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/organizations": { - "get": { - "tags": [ - "People" - ], - "operationId": "list_person_organizations", - "summary": "List organizations related to a person", - "description": "The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityOrganizationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/products": { - "get": { - "tags": [ - "People" - ], - "operationId": "list_person_products", - "summary": "List products related to a person", - "description": "The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityProductListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/people/{slug}/channels": { - "get": { - "tags": [ - "People" - ], - "operationId": "list_person_channels", - "summary": "List channels related to a person", - "description": "The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityChannelListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "get_topic", - "summary": "Get a topic entity page", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TopicPageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}/topics": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "list_topic_topics", - "summary": "List topics related to a topic", - "description": "The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityTopicListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}/people": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "list_topic_people", - "summary": "List people related to a topic", - "description": "The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityPeopleListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}/organizations": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "list_topic_organizations", - "summary": "List organizations related to a topic", - "description": "The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityOrganizationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}/products": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "list_topic_products", - "summary": "List products related to a topic", - "description": "The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityProductListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/topics/{slug}/channels": { - "get": { - "tags": [ - "Topics" - ], - "operationId": "list_topic_channels", - "summary": "List channels related to a topic", - "description": "The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityChannelListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "get_organization", - "summary": "Get a organization entity page", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/OrganizationPageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}/topics": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "list_organization_topics", - "summary": "List topics related to a organization", - "description": "The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityTopicListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}/people": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "list_organization_people", - "summary": "List people related to a organization", - "description": "The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityPeopleListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}/organizations": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "list_organization_organizations", - "summary": "List organizations related to a organization", - "description": "The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityOrganizationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}/products": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "list_organization_products", - "summary": "List products related to a organization", - "description": "The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityProductListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/organizations/{slug}/channels": { - "get": { - "tags": [ - "Organizations" - ], - "operationId": "list_organization_channels", - "summary": "List channels related to a organization", - "description": "The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityChannelListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}": { - "get": { - "tags": [ - "Products" - ], - "operationId": "get_product", - "summary": "Get a product entity page", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ProductPageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}/topics": { - "get": { - "tags": [ - "Products" - ], - "operationId": "list_product_topics", - "summary": "List topics related to a product", - "description": "The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityTopicListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}/people": { - "get": { - "tags": [ - "Products" - ], - "operationId": "list_product_people", - "summary": "List people related to a product", - "description": "The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityPeopleListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}/organizations": { - "get": { - "tags": [ - "Products" - ], - "operationId": "list_product_organizations", - "summary": "List organizations related to a product", - "description": "The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityOrganizationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}/products": { - "get": { - "tags": [ - "Products" - ], - "operationId": "list_product_products", - "summary": "List products related to a product", - "description": "The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityProductListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/products/{slug}/channels": { - "get": { - "tags": [ - "Products" - ], - "operationId": "list_product_channels", - "summary": "List channels related to a product", - "description": "The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" - }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityChannelListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "get_channel", - "summary": "Get a channel entity page", - "description": "Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" + "description": "The monitor the tracker belongs to. Null for trackers outside a monitor." } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChannelPageResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" + }, + "required": [ + "id", + "tracker_id", + "monitor_id", + "entity_id", + "mention_id", + "video_id", + "excerpt_id", + "evidence_kind", + "channel", + "status", + "final_status", + "error_message", + "scheduled_at", + "sent_at", + "created_at", + "tracker", + "monitor" + ] + }, + "MonitorAddTrackersResponse": { + "type": "object", + "properties": { + "attached_count": { + "type": "integer", + "description": "Number of unique requested trackers attached." }, - "429": { - "$ref": "#/components/responses/RateLimited" + "message": { + "type": "string", + "description": "Human-readable confirmation, e.g. \"Added 3 tracker(s) to monitor\"." }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/topics": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_topics", - "summary": "List topics related to a channel", - "description": "The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] + "monitor_id": { + "type": "string", + "description": "The monitor id from the request path." } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + }, + "required": [ + "attached_count", + "message", + "monitor_id" + ] + }, + "MonitorAddEntitiesResponse": { + "type": "object", + "properties": { + "monitor_id": { + "type": "string", + "description": "The monitor the entities were added to." }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MonitorEntityResult" }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "description": "One result per distinct requested entity id, in request order." + } + }, + "required": [ + "monitor_id", + "results" + ] + }, + "MonitorEntityResult": { + "type": "object", + "properties": { + "entity_id": { + "type": "string", + "description": "The entity id as requested." }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "canonical_entity_id": { + "type": "string", + "description": "Present when entity_id was merged: the canonical entity the tracker follows." }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "tracker_id": { + "type": [ + "string", + "null" + ], + "description": "The tracker that follows the entity: an existing one when the account already tracked it, else the one this request created. Null when no tracker could be used (see reason)." }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "created": { + "type": "boolean", + "description": "true when this request created the tracker." }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "attached": { + "type": "boolean", + "description": "true when the tracker is in this monitor after the request, including when it already was." }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" + "reason": { + "type": "string", + "enum": [ + "entity_not_found", + "entity_type_not_trackable", + "tracker_limit_reached", + "tracked_in_another_monitor" + ], + "description": "Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants)." + }, + "current_monitor_id": { + "type": "string", + "description": "With reason tracked_in_another_monitor: the monitor the tracker is in." } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" + }, + "required": [ + "entity_id", + "tracker_id", + "created", + "attached" + ] + }, + "SlackIntegrationListResponse": { + "type": "object", + "properties": { + "integrations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Slack integration id. Pass it as slack_integration_id when creating or updating a monitor with notify_slack: true." + }, + "team_name": { + "type": "string", + "description": "The Slack workspace name." + }, + "default_channel_id": { + "type": [ + "string", + "null" + ], + "description": "The channel the workspace install chose. A monitor with notify_slack and no slack_channel_id delivers here. Null when the install chose none; then pass slack_channel_id." + }, + "channels": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Slack channel id." + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "Channel name as stored at install time, without the #. Null when not stored." + } + }, + "required": [ + "id", + "name" + ] + }, + "description": "The channels Arcmira has stored for this workspace: today the install channel only. Arcmira does not list the workspace live." + } }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } + "required": [ + "id", + "team_name", + "default_channel_id", + "channels" + ] }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityTopicListResponse" - } - } - } + "description": "Active Slack workspaces connected to the account, ordered by workspace name. Empty when none is connected: connect one in the dashboard first." + } + }, + "required": [ + "integrations" + ] + }, + "TrackerListResponse": { + "type": "object", + "properties": { + "trackers": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Tracker" + }, + "description": "All trackers for the account, newest first." }, - "400": { - "$ref": "#/components/responses/InvalidRequest" + "count": { + "type": "integer", + "description": "Number of trackers returned." + } + }, + "required": [ + "trackers", + "count" + ] + }, + "Tracker": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Tracker id in the form \"trk_{hex}\"." }, - "401": { - "$ref": "#/components/responses/AuthenticationError" + "entity_name": { + "type": "string", + "description": "The tracked entity name, as submitted." }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" + "entity_type": { + "type": "string", + "description": "The tracked entity type. Values: person, organization, product, topic, channel." }, - "403": { - "$ref": "#/components/responses/PermissionError" + "display_name": { + "type": "string", + "description": "User-facing display name. Falls back to entity_name when not customized." }, - "404": { - "$ref": "#/components/responses/NotFound" + "notify_email": { + "type": "boolean", + "description": "True when this tracker delivers by email (default true at creation)." }, - "429": { - "$ref": "#/components/responses/RateLimited" + "notify_webhook": { + "type": "boolean", + "description": "True when this tracker has a per-tracker webhook override enabled." }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/people": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_people", - "summary": "List people related to a channel", - "description": "The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" + "notify_slack": { + "type": "boolean", + "description": "True when this tracker has a per-tracker Slack override enabled." }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 - }, - "required": false, - "name": "limit", - "in": "query" + "webhook_url": { + "type": [ + "string", + "null" + ], + "description": "Per-tracker webhook destination override. Null when the tracker uses its monitor's delivery settings. Absent when the tracker is in a team monitor the caller does not own." }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + "slack_channel_id": { + "type": [ + "string", + "null" + ], + "description": "Per-tracker Slack channel override. Null when not set." }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "slack_integration_id": { + "type": [ + "string", + "null" + ], + "description": "Per-tracker Slack integration override. Null when not set." }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "filters": { + "type": [ + "object", + "null" + ], + "additionalProperties": {}, + "description": "Optional matching filters as submitted. Null when none were set." }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "paused": { + "type": "boolean", + "description": "True when the tracker is paused." }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "paused_at": { + "type": [ + "string", + "null" + ], + "description": "When the tracker was paused. Null unless paused." + }, + "last_notified_at": { + "type": [ + "string", + "null" + ], + "description": "When the tracker last produced an alert. Null until the first alert." + }, + "created_at": { + "type": "string", + "description": "When the tracker was created." + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "description": "When the tracker was last updated." }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "monitor_id": { + "type": "string", + "description": "The monitor this tracker belongs to. Absent for standalone trackers." }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" + "email_delivery_count": { + "type": "integer", + "description": "Email deliveries in the current billing period." + }, + "webhook_delivery_count": { + "type": "integer", + "description": "Webhook deliveries in the current billing period." + }, + "slack_delivery_count": { + "type": "integer", + "description": "Slack deliveries in the current billing period." } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityPeopleListResponse" - } - } - } + }, + "required": [ + "id", + "entity_name", + "entity_type", + "display_name", + "notify_email", + "notify_webhook", + "notify_slack", + "slack_channel_id", + "slack_integration_id", + "filters", + "paused", + "paused_at", + "last_notified_at", + "created_at", + "updated_at", + "email_delivery_count", + "webhook_delivery_count", + "slack_delivery_count" + ] + }, + "TrackerMutationResponse": { + "type": "object", + "properties": { + "tracker": { + "$ref": "#/components/schemas/Tracker" }, - "400": { - "$ref": "#/components/responses/InvalidRequest" + "message": { + "type": "string", + "description": "Human-readable confirmation." + } + }, + "required": [ + "tracker", + "message" + ] + }, + "MessageResponse": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Human-readable confirmation." + } + }, + "required": [ + "message" + ] + }, + "TranscriptResponse": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "ready" + ] }, - "401": { - "$ref": "#/components/responses/AuthenticationError" + "video": { + "$ref": "#/components/schemas/TranscriptVideo" }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" + "quality": { + "type": "string", + "enum": [ + "captions", + "premium" + ], + "description": "The requested quality. Premium is served only with an owned unlock; it never falls back to captions." }, - "403": { - "$ref": "#/components/responses/PermissionError" + "source": { + "type": "string", + "enum": [ + "creator_captions", + "third_party_quick", + "arcmira_premium" + ], + "description": "Public source class. creator_captions were written or approved by the channel, third_party_quick are YouTube automatic captions, arcmira_premium is our own diarized transcript." }, - "404": { - "$ref": "#/components/responses/NotFound" + "language": { + "type": "string", + "description": "The resolved track code, asr-en style when the track is automatic." }, - "429": { - "$ref": "#/components/responses/RateLimited" + "languages": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CaptionTrack" + }, + "description": "Every caption track the video offers. Empty when we did not list them on this call." }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/organizations": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_organizations", - "summary": "List organizations related to a channel", - "description": "The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." + "lines": { + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "type": "number", + "description": "Line start in seconds from the beginning of the video." + }, + "end": { + "type": "number", + "description": "Line end in seconds." + }, + "text": { + "type": "string" + }, + "speaker": { + "type": "integer", + "description": "The person saying this line, present on every Premium line. Join it against speakers[].id." + }, + "index": { + "type": "integer", + "description": "Line index, present on every Premium line. Echo it as anchor.segmentIndex when you correct the line." + } + }, + "required": [ + "start", + "end", + "text" + ] }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" + "description": "Present when timestamps is true. Cite start with watch_url." }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 + "paragraphs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "start": { + "type": "number", + "description": "Paragraph start in seconds." + }, + "text": { + "type": "string" + }, + "speaker": { + "type": "integer" + } + }, + "required": [ + "start", + "text" + ] }, - "required": false, - "name": "limit", - "in": "query" + "description": "Present when timestamps is false. Lines joined on speaker changes for Premium and on sentence boundaries for captions." }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." + "speakers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "x-arcmira-ordinal": true, + "description": "Speaker id, numbered in the order people first speak. Only meaningful with this read and its revision." + }, + "name": { + "type": "string", + "description": "The identified person, or Speaker 1, Speaker 2 and so on for a voice nobody has identified yet." + }, + "entity_id": { + "type": [ + "string", + "null" + ], + "description": "Public entity id (\"ent_{n}\") of the identified person. Null when the speaker is unidentified." + }, + "confidence": { + "type": [ + "string", + "null" + ], + "description": "high when the name was reviewed, low when it is your own identification still awaiting review, null when nobody is identified." + } + }, + "required": [ + "id", + "name", + "entity_id", + "confidence" + ] }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + "description": "Premium only. Speaker identification is right most of the time and wrong sometimes; say it came from Arcmira when a name matters." }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "revision": { + "type": "string", + "description": "Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. Echo it on every correction; a 409 means it changed underneath you, so read again." }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." + "range": { + "type": "object", + "properties": { + "start": { + "type": "number" + }, + "end": { + "type": "number" + } }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "required": [ + "start", + "end" + ], + "description": "Echoed when you sent start and end. Lines overlapping the window are returned. On captions only the window is billed; Premium retrieval is free." }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "rows_billed": { + "type": "integer", + "description": "Rows this call charged. 0 on a repeat of the same video, quality, language, and range inside the 7 day dedupe window, and always 0 on Premium retrieval." }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "as_of": { + "type": [ + "string", + "null" + ], + "description": "When the transcript was produced." }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "premium_job": { + "$ref": "#/components/schemas/TranscriptionJob" }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" + "access": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "invalid_request_error", + "authentication_error", + "permission_error", + "quota_exceeded", + "rate_limit_error", + "not_found", + "conflict_error", + "server_error" + ], + "description": "The error class. It fixes the HTTP status: invalid_request_error 400, authentication_error 401, quota_exceeded 402, permission_error 403, not_found 404, conflict_error 409, rate_limit_error 429, server_error 500. Switch on it for retry and gate handling." }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" + "code": { + "type": "string", + "description": "The specific condition, stable and snake_case; doc_url anchors on it. x-arcmira-codes on this schema lists every code with its type, gate and meaning. The list is open: new codes may appear inside an existing type, so switch on type and gate first." }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" + "reason": { + "type": "string", + "enum": [ + "no_credential", + "invalid", + "revoked" + ], + "description": "Only on invalid_api_key. no_credential: nothing was sent. invalid: a credential was sent and is unknown or malformed. revoked: the key exists and is no longer usable." }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" + "message": { + "type": "string", + "description": "One plain line. Names the fix or the unlock." }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" + "param": { + "type": "string", + "description": "The query or body parameter the gate refused, when one did." + }, + "gate": { + "type": "string", + "enum": [ + "rows", + "key", + "plan", + "freshness", + "exposure_law", + "rate", + "pagination" + ], + "description": "Which boundary refused. Present on every gate error; switch on it without parsing the message." + }, + "resource": { + "$ref": "#/components/schemas/ErrorResource" + }, + "unlock": { + "type": "object", + "properties": { + "tier": { + "type": "string", + "description": "The plan that lifts the gate." + }, + "url": { + "type": "string", + "description": "Absolute upgrade or sign-up URL carrying its ?src= attribution. Use it verbatim." + }, + "offer": { + "type": "null", + "description": "Reserved for the agent-discount offer. Always null today." + }, + "action": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "description": "What the call does. send_signup_code sends a verification code to an address for an account key." + }, + "method": { + "type": "string", + "description": "HTTP method to use." + }, + "url": { + "type": "string", + "description": "Absolute endpoint carrying its ?src= attribution. Call it verbatim." + } + }, + "required": [ + "kind", + "method", + "url" + ], + "description": "The request that lifts the gate with no human, present when the caller can fix this itself. A key gate carries the signup send here; a plan or quota gate has no action and its url is a page a person opens." + } + }, + "required": [ + "tier", + "url", + "offer" + ], + "description": "How to lift the gate. Present when the gate has an unlock." + }, + "retry_after_seconds": { + "type": "integer", + "description": "Present on rate gates. Mirrors the Retry-After header." + }, + "current_revision": { + "type": "string", + "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." + }, + "expected_seq": { + "type": "integer", + "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." + }, + "details": { + "type": "object", + "properties": { + "quote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." + }, + "existing_request_id": { + "type": "string", + "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + }, + "existing_id": { + "type": "string", + "description": "On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker." + } + }, + "description": "Machine data the refusal carries for you to act on. Present only on the codes that name a field here." + }, + "doc_url": { + "type": "string" + }, + "request_id": { + "type": "string" } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityOrganizationListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "429": { - "$ref": "#/components/responses/RateLimited" + "required": [ + "type", + "code", + "message", + "doc_url", + "request_id" + ], + "description": "The gate that reduced this response. Present only when something was withheld; carries the same code, gate, and unlock an outright refusal would." }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/products": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_products", - "summary": "List products related to a channel", - "description": "The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] + "note": { + "type": "string", + "description": "One steering sentence for the agent reading this. On Premium it is the diarization disclosure verbatim." } + }, + "required": [ + "state", + "video", + "quality", + "source", + "language", + "languages", + "rows_billed", + "as_of", + "note" ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" - }, + "examples": [ { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 + "video": { + "id": "dQw4w9WgXcQ", + "title": "TBPN | Tuesday, August 4", + "channel_id": "UC-DRzaGnL_vtBUpCFH5M0tg", + "channel_name": "TBPN", + "published_at": "2026-08-04T17:00:00.000Z", + "duration_seconds": 10800, + "watch_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, - "required": false, - "name": "limit", - "in": "query" + "quality": "captions", + "source": "creator_captions", + "language": "en", + "languages": [ + { + "code": "en", + "name": "English", + "generated": false + } + ], + "lines": [ + { + "start": 4787, + "end": 4791.5, + "text": "Ramp has been on the show for a while now." + }, + { + "start": 4791.5, + "end": 4796, + "text": "The pitch is still the same, spend less time on expenses." + } + ], + "rows_billed": 12, + "as_of": "2026-08-04T18:12:00.000Z", + "note": "This transcript is the video's own caption track. `creator_captions` were written or approved by the channel; `third_party_quick` are YouTube's automatic captions and can misspell names and drop punctuation. `language` says which track you got." }, { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." + "video": { + "id": "aB3dE5fG7hI", + "title": "Moment of Truth | The permitting fight nobody watched", + "channel_id": "UClWkDGXEzsh77GAhs90wpXw", + "channel_name": "Moment of Truth", + "published_at": "2026-08-19T14:00:00.000Z", + "duration_seconds": 4500, + "watch_url": "https://www.youtube.com/watch?v=aB3dE5fG7hI" }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + "quality": "premium", + "source": "arcmira_premium", + "language": "en", + "languages": [ + { + "code": "en", + "name": "English", + "generated": false + } + ], + "lines": [ + { + "start": 612, + "end": 617.5, + "text": "The permit sat in review for nineteen months.", + "speaker": 0, + "index": 0 + }, + { + "start": 617.5, + "end": 623, + "text": "And the second review started before the first one closed.", + "speaker": 1, + "index": 1 + } + ], + "speakers": [ + { + "id": 0, + "name": "Dana Whitfield", + "entity_id": 4821, + "confidence": "high" + }, + { + "id": 1, + "name": "Speaker 1", + "entity_id": null, + "confidence": null + } + ], + "revision": "rev_7f2c1a.0k3m9x", + "rows_billed": 375, + "as_of": "2026-08-19T16:40:00.000Z", + "note": "Premium transcripts are Arcmira's own. Every Premium transcript is diarized: each line carries a speaker. We identify each speaker from the audio, the video, and internal and community review. We are right most of the time and wrong sometimes, so when a name matters, say it came from Arcmira's speaker identification and cite the line." + } + ] + }, + "TranscriptVideo": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "YouTube video id (11 characters)." }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "title": { + "type": "string", + "description": "Video title. Empty when we could not read it." }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "channel_id": { + "type": [ + "string", + "null" + ], + "description": "YouTube channel id of the source channel." }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "channel_name": { + "type": [ + "string", + "null" + ] }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "published_at": { + "type": [ + "string", + "null" + ], + "description": "Publish timestamp. Cite it as the date of anything you quote." }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "duration_seconds": { + "type": [ + "number", + "null" + ], + "description": "Video length in seconds. Null when unknown, which also means the row estimate was unknown." }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" + "watch_url": { + "type": "string", + "description": "Canonical YouTube watch URL." } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityProductListResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" + }, + "required": [ + "id", + "title", + "channel_id", + "channel_name", + "published_at", + "duration_seconds", + "watch_url" + ] + }, + "CaptionTrack": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "Caption track code, e.g. en or de. Pass it as language to select this track." }, - "401": { - "$ref": "#/components/responses/AuthenticationError" + "name": { + "type": "string", + "description": "Track name as YouTube reports it." }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" + "generated": { + "type": "boolean", + "description": "True for YouTube automatic captions, false for a track the channel wrote or approved." + } + }, + "required": [ + "code", + "name", + "generated" + ] + }, + "TranscriptionJob": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Transcription request id (UUID)." }, - "403": { - "$ref": "#/components/responses/PermissionError" + "video_id": { + "type": "string", + "description": "YouTube video id (11 characters)." }, - "404": { - "$ref": "#/components/responses/NotFound" + "state": { + "type": "string", + "enum": [ + "pending", + "ready", + "failed", + "refunded" + ], + "description": "Coarse outcome: pending until the Premium transcript is servable (ready), the purchase failed, or it was refunded." }, - "429": { - "$ref": "#/components/responses/RateLimited" + "status": { + "type": "string", + "enum": [ + "queued", + "downloading", + "transcribing", + "analyzing", + "complete", + "failed", + "refund_pending", + "refunded" + ], + "description": "Request status. Values: queued (accepted; audio download not started), downloading (fetching the video audio), transcribing (premium speech-to-text is running), analyzing (entity/commercial analysis is running), complete (premium transcript is servable via GET /v1/transcripts/{video_id}), failed (rejected intent or legacy purchase requiring accounting review), refund_pending (refund transaction must still complete), refunded (terminal failure; the charged rows were returned and the unlock this submission bought was revoked)." }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/channels": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_channels", - "summary": "List channels related to a channel", - "description": "The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" + "stage": { + "type": [ + "string", + "null" + ], + "enum": [ + "queued", + "transcribing", + "analyzing", + null + ], + "description": "User-facing stage: downloading folds into transcribing. Values: queued (waiting to start), transcribing (downloading or transcribing), analyzing (analysis running). Null for terminal statuses and refund_pending." }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "credits" + ] + }, + "amount": { + "type": "number", + "description": "Credits this purchase charged. 0 when a prior unlock made it free." + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the credits came from: the included allowance, on-demand usage, or both. Present once the purchase is funded." + } }, - "required": false, - "name": "limit", - "in": "query" + "required": [ + "unit", + "amount" + ], + "description": "What the purchase charged. Present on durable purchases; absent only on legacy requests." }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + "eta_seconds": { + "type": "integer", + "description": "Estimated seconds until completion, re-derived from live pipeline telemetry on every poll. Only present while the request is in flight; absent on refund_pending, which has no completion ETA." }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." - }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "next_poll_seconds": { + "type": "integer", + "description": "Seconds to sleep before the next poll (also sent as the Retry-After header). Only present while the request is in flight." }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "error": { + "type": "string", + "description": "Failure reason. Only present when state is failed or refunded." }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "refunded": { + "type": "boolean", + "description": "True when the charge was returned. Only present when state is failed or refunded." }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "created_at": { + "type": "string", + "description": "When the request was submitted." }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." - }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "completed_at": { + "type": "string", + "description": "When the request reached a terminal status. Absent while in flight." }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" + "status_url": { + "type": "string", + "description": "Absolute URL of GET /v1/transcriptions/{id} for this job." } + }, + "required": [ + "id", + "video_id", + "state", + "status", + "stage", + "created_at", + "status_url" ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EntityChannelListResponse" - } - } - } + "description": "Your open Premium purchase for this video, when captions were served while it prepares." + }, + "TranscriptPending": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "pending" + ] }, - "400": { - "$ref": "#/components/responses/InvalidRequest" + "quality": { + "type": "string", + "enum": [ + "premium" + ] }, - "401": { - "$ref": "#/components/responses/AuthenticationError" + "video_id": { + "type": "string" }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" + "job": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptionJob" + }, + { + "description": "The Premium purchase this read started or joined. Read this transcript again after Retry-After; job.status_url polls the same purchase." + } + ] + } + }, + "required": [ + "state", + "quality", + "video_id", + "job" + ] + }, + "TranscriptPurchaseQuote": { + "type": "object", + "properties": { + "video_id": { + "type": "string" }, - "403": { - "$ref": "#/components/responses/PermissionError" + "duration_seconds": { + "type": "number" }, - "404": { - "$ref": "#/components/responses/NotFound" + "billing_scope": { + "type": "string", + "enum": [ + "full_video" + ] }, - "429": { - "$ref": "#/components/responses/RateLimited" + "owned": { + "type": "boolean" }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/channels/{slug}/guests": { - "get": { - "tags": [ - "Channels" - ], - "operationId": "list_channel_guests", - "summary": "List channel guests", - "description": "People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it." - }, - "required": true, - "description": "The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it.", - "name": "slug", - "in": "path" + "eligible": { + "type": "boolean" }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20 + "upgrade": { + "type": "object", + "properties": { + "label": { + "type": "string" + }, + "href": { + "type": "string" + } }, - "required": false, - "name": "limit", - "in": "query" + "required": [ + "label", + "href" + ], + "description": "Present when eligible is false: the plan checkout that can buy this transcript, as a button label and an absolute link." }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live." - }, - "required": false, - "description": "Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live.", - "name": "cursor", - "in": "query" + "quote": { + "$ref": "#/components/schemas/TranscriptQuote" }, - { - "schema": { - "type": "string", - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description)." + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } }, - "required": false, - "description": "Substring filter over the row's text columns (e.g. video title, channel name, description).", - "name": "q", - "in": "query" + "required": [ + "unit", + "amount", + "from" + ] }, - { - "schema": { - "type": "string", - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns)." - }, - "required": false, - "description": "Restrict the q filter to one column. Default \"any\" (all searchable columns).", - "name": "field", - "in": "query" + "credits_per_row": { + "type": "number" }, - { - "schema": { - "type": "string", - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\")." - }, - "required": false, - "description": "Sort key. Rows default to newest first; supported values vary by list (e.g. \"date\", \"channel\").", - "name": "sort", - "in": "query" + "max_on_demand_cents": { + "type": "number" }, - { - "schema": { - "type": "string", - "enum": [ - "asc", - "desc" - ], - "description": "Sort direction. Default desc." - }, - "required": false, - "description": "Sort direction. Default desc.", - "name": "order", - "in": "query" + "on_demand_cents_per_unit": { + "type": "number" }, - { - "schema": { - "type": "string", - "enum": [ - "appearances", - "mentions" - ], - "description": "Person relationship lens: guest appearances or inbound mentions." + "refund_policy": { + "type": "string" + } + }, + "required": [ + "video_id", + "duration_seconds", + "billing_scope", + "owned", + "eligible", + "quote", + "charge", + "credits_per_row", + "max_on_demand_cents", + "on_demand_cents_per_unit", + "refund_policy" + ] + }, + "TranscriptionListResponse": { + "type": "object", + "properties": { + "requests": { + "type": "array", + "items": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptionJob" + }, + { + "type": "object", + "properties": { + "title": { + "type": [ + "string", + "null" + ], + "description": "Video title for display. Null when unknown." + } + }, + "required": [ + "title" + ] + } + ] }, - "required": false, - "description": "Person relationship lens: guest appearances or inbound mentions.", - "name": "mode", - "in": "query" + "description": "Your requests in descending creation time and id order, up to the requested limit." }, - { - "schema": { - "type": "string", - "enum": [ - "0", - "1", - "true", - "false" - ], - "description": "For person appearances, true lists guest episodes and false lists inbound mentions." - }, - "required": false, - "description": "For person appearances, true lists guest episodes and false lists inbound mentions.", - "name": "is_appearance", - "in": "query" + "has_more": { + "type": "boolean", + "description": "True when another page exists in this traversal." + }, + "next_cursor": { + "type": [ + "string", + "null" + ], + "description": "Signed continuation for the same filter, limit and credential; null on the last page." } + }, + "required": [ + "requests", + "has_more", + "next_cursor" + ] + } + }, + "parameters": {} + }, + "paths": { + "/v1/health": { + "get": { + "tags": [ + "Meta" ], + "operationId": "get_health", + "summary": "Health check", + "security": [], "responses": { "200": { "description": "Success", @@ -20292,21 +6689,66 @@ }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthResponse" + } + } + } + }, + "429": { + "description": "Rate limit exceeded. Retry-After carries the wait.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/v1/openapi.json": { + "get": { + "tags": [ + "Meta" + ], + "operationId": "get_openapi_document", + "summary": "OpenAPI document", + "security": [], + "responses": { + "200": { + "description": "This OpenAPI 3.1 document.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChannelGuestListResponse" + "$ref": "#/components/schemas/OpenApiDocument" } } } @@ -20314,20 +6756,29 @@ "400": { "$ref": "#/components/responses/InvalidRequest" }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { - "$ref": "#/components/responses/RateLimited" + "description": "Rate limit exceeded. Retry-After carries the wait.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, "500": { "$ref": "#/components/responses/ServerError" @@ -20335,14 +6786,14 @@ } } }, - "/v1/monitors": { + "/v1/me": { "get": { "tags": [ - "Monitors" + "Meta" ], - "operationId": "list_monitors", - "summary": "List monitors", - "description": "All monitors for the account with tracker counts, alert counts for the current calendar month, and Slack display metadata. Single page, no pagination.", + "operationId": "get_me", + "summary": "Current API key context", + "description": "Available even when the account has exhausted its usage allowance. Returns the credential making the request (key_id, key_label, credential_kind), the masked account email, the tier, scopes, rate limit, row usage with period_resets_at, and account settings. settings.transcripts is what a transcript request that names no parameter of its own receives: every key of the account resolves against it.", "security": [ { "bearerAuth": [] @@ -20371,7 +6822,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MonitorListResponse" + "$ref": "#/components/schemas/MeResponse" } } } @@ -20395,131 +6846,60 @@ "$ref": "#/components/responses/ServerError" } } - }, - "post": { + } + }, + "/v1/me/settings": { + "patch": { "tags": [ - "Monitors" + "Meta" ], - "operationId": "create_monitor", - "summary": "Create monitor", - "description": "Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint.", + "operationId": "update_my_settings", + "summary": "Set account defaults", + "description": "Sets the account defaults every key of the account resolves against. Send only the fields you are changing; an omitted field keeps the value it has. Resolution order for every transcript request is the explicit parameter, then these settings, then the platform default, so a default never overrides a parameter the caller sent. Defaults live on the account, never on a key: two keys of one account answer the same request the same way. The response echoes the resolved settings.", "security": [ { "bearerAuth": [] } ], - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." - } - ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Display name (1-100 characters). Required on create." - }, - "notifyEmails": { - "type": "array", - "items": { - "type": "string", - "format": "email" + "transcripts": { + "type": "object", + "properties": { + "quality": { + "type": "string", + "enum": [ + "captions", + "premium" + ], + "description": "Default transcript quality for this account: captions or premium. premium reads require an existing purchase. Owned transcripts remain readable after a plan downgrade; new purchases require an eligible plan." + }, + "language": { + "type": "string", + "description": "Default comma-separated caption language priority list, tried in order (e.g. \"de,en\"), at most 5 codes. Use asr for the first automatic track and asr- for a specific one." + }, + "timestamps": { + "type": "boolean", + "description": "Default for the timestamps parameter. false makes paragraphs[] the default body shape instead of lines[]." + } }, - "maxItems": 20, - "description": "Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []." - }, - "notifyFrequency": { - "type": "string", - "enum": [ - "realtime", - "hourly", - "daily" - ], - "description": "Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent." - }, - "digestDay": { - "type": "string", - "description": "Digest day of week. Default \"monday\". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies." - }, - "digestTime": { - "type": "string", - "description": "Digest send hour as HH:MM (account timezone). Default \"09:00\". Applies to daily digests." - }, - "notifyWebhook": { - "type": "boolean", - "description": "Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter." - }, - "webhookUrl": { - "type": "string", - "description": "Destination URL for webhook alert deliveries." - }, - "notifySlack": { - "type": "boolean", - "description": "Enable Slack delivery. Requires a Slack integration connected in the dashboard." - }, - "slackIntegrationId": { - "type": "string", - "description": "Slack integration id from the dashboard OAuth flow." - }, - "slackChannelId": { - "type": "string", - "description": "Slack channel id to deliver to." + "description": "Fields to change. An omitted field keeps the value the account already carries." } }, "required": [ - "name" - ], - "additionalProperties": false + "transcripts" + ] } } } }, "responses": { "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MonitorMutationResponse" - } - } - } - }, - "201": { - "description": "Monitor created. When this request enabled webhook signing, monitor.webhookSecret is present and recoverable by retrying with the original Idempotency-Key during its recovery window.", + "description": "Settings updated", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -20533,51 +6913,30 @@ "RateLimit-Remaining": { "$ref": "#/components/headers/RateLimit-Remaining" }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/MonitorMutationResponse" - } - } - } - }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimit-Reset" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/MeSettingsResponse" } } } }, + "400": { + "$ref": "#/components/responses/InvalidRequest" + }, + "401": { + "$ref": "#/components/responses/AuthenticationError" + }, + "403": { + "$ref": "#/components/responses/PermissionError" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -20587,144 +6946,54 @@ } } }, - "/v1/monitors/{id}": { - "patch": { + "/v1/signups": { + "post": { "tags": [ - "Monitors" - ], - "operationId": "update_monitor", - "summary": "Update monitor", - "description": "A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter.", - "security": [ - { - "bearerAuth": [] - } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Monitor id." - }, - "required": true, - "description": "Monitor id.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." - } + "Meta" ], + "operationId": "create_signup", + "summary": "Send a verification code to an address", + "description": "Starts the signup that ends in an account key, with no key and no login. Sends a 6 digit code to the address, valid for 10 minutes and 5 attempts, and answers 202 with the verify call. Sends are capped at 3 per address per hour, 10 per IP per hour, and 25 per client fingerprint per day; past a cap the response is 429 signup_send_limited with retry_after_seconds and an unlock whose action is this call. An address that already has an account gets a code too; verifying it mints a key on that account. Send { \"email\": \"agent@example.com\" }, with an optional \"src\" naming the surface that sent you.", + "security": [], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Display name (1-100 characters). Required on create." - }, - "notifyEmails": { - "type": "array", - "items": { - "type": "string", - "format": "email" - }, - "maxItems": 20, - "description": "Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []." - }, - "notifyFrequency": { - "type": "string", - "enum": [ - "realtime", - "hourly", - "daily" - ], - "description": "Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent." - }, - "digestDay": { - "type": "string", - "description": "Digest day of week. Default \"monday\". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies." - }, - "digestTime": { - "type": "string", - "description": "Digest send hour as HH:MM (account timezone). Default \"09:00\". Applies to daily digests." - }, - "notifyWebhook": { - "type": "boolean", - "description": "Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter." - }, - "webhookUrl": { - "type": "string", - "description": "Destination URL for webhook alert deliveries." - }, - "notifySlack": { - "type": "boolean", - "description": "Enable Slack delivery. Requires a Slack integration connected in the dashboard." - }, - "slackIntegrationId": { + "email": { "type": "string", - "description": "Slack integration id from the dashboard OAuth flow." + "maxLength": 254, + "description": "The address the verification code is sent to. Case is folded; the same address in any casing is one account and one send budget." }, - "slackChannelId": { + "src": { "type": "string", - "description": "Slack channel id to deliver to." - }, - "isPaused": { - "type": "boolean", - "description": "Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued." - }, - "isCollapsed": { - "type": "boolean", - "description": "Dashboard display state." - }, - "sortOrder": { - "type": "integer", - "description": "Dashboard sort position." + "maxLength": 32, + "description": "The agent surface that sent you, as a value from the ?src= registry. Also accepted as ?src= on the URL. The unlock in a refusal carries it back." } }, - "additionalProperties": false + "required": [ + "email" + ] } } } }, "responses": { - "200": { - "description": "Success", + "202": { + "description": "Code sent", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MonitorMutationResponse" + "$ref": "#/components/schemas/SignupSentResponse" } } } @@ -20732,23 +7001,20 @@ "400": { "$ref": "#/components/responses/InvalidRequest" }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "429": { + "description": "Rate limit exceeded. Retry-After carries the wait.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" } }, "content": { @@ -20759,99 +7025,103 @@ } } }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, "500": { "$ref": "#/components/responses/ServerError" + }, + "503": { + "description": "server_error. The verification email could not be sent. Retry-After carries the wait.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } - }, - "delete": { + } + }, + "/v1/signups/verify": { + "post": { "tags": [ - "Monitors" - ], - "operationId": "delete_monitor", - "summary": "Delete monitor", - "description": "Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again.", - "security": [ - { - "bearerAuth": [] - } + "Meta" ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Monitor id." - }, - "required": true, - "description": "Monitor id.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." + "operationId": "verify_signup", + "summary": "Exchange a verification code for an account key", + "description": "Consumes the code POST /v1/signups sent, creates the account when the address has none, and mints an arc_sk_ key on it: the read scope, the free tier's lifetime row pool, no expiry. A wrong, expired, or spent code is 400 signup_code_invalid on param code, naming the attempts left; its unlock action is a new send. Send { \"email\": \"agent@example.com\", \"code\": \"482913\" }.", + "security": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "maxLength": 254, + "description": "The address the code was sent to." + }, + "code": { + "type": "string", + "pattern": "^\\d+$", + "description": "The six digit code from the email. Ten minutes, five attempts, then a new send is required." + } + }, + "required": [ + "email", + "code" + ] + } + } } - ], + }, "responses": { - "200": { - "description": "Success", + "201": { + "description": "Account key minted", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MonitorDeleteResponse" + "$ref": "#/components/schemas/SignupVerifiedResponse" } } } }, - "400": { - "$ref": "#/components/responses/InvalidRequest" - }, - "401": { - "$ref": "#/components/responses/AuthenticationError" - }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, + "400": { + "$ref": "#/components/responses/InvalidRequest" + }, "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "429": { + "description": "Rate limit exceeded. Retry-After carries the wait.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" } }, "content": { @@ -20862,23 +7132,20 @@ } } }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, "500": { "$ref": "#/components/responses/ServerError" } } } }, - "/v1/monitors/{id}/webhook-secret/rotate": { - "post": { + "/v1/entities/resolve": { + "get": { "tags": [ - "Monitors" + "Entities" ], - "operationId": "rotate_monitor_webhook_secret", - "summary": "Rotate the monitor webhook signing secret", - "description": "Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope.", + "operationId": "resolve_entity", + "summary": "A name to one id, before any filter", + "description": "Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name (\"the startup bank\", \"on My First Million\"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows.", "security": [ { "bearerAuth": [] @@ -20888,22 +7155,68 @@ { "schema": { "type": "string", - "description": "Monitor id." + "minLength": 2, + "description": "A name, @handle, YouTube URL or channel id (UC...). One thing per call." }, "required": true, - "description": "Monitor id.", - "name": "id", - "in": "path" + "description": "A name, @handle, YouTube URL or channel id (UC...). One thing per call.", + "name": "q", + "in": "query" }, { "schema": { - "type": "string" + "type": "string", + "enum": [ + "person", + "organization", + "product", + "topic", + "channel" + ], + "description": "Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id." }, "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." + "description": "Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id.", + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 15, + "default": 8, + "description": "Candidates to return, 1 to 15. Default 8." + }, + "required": false, + "description": "Candidates to return, 1 to 15. Default 8.", + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 2, + "maxLength": 300, + "description": "What the user said about the name, in their words (\"the startup bank\", \"Canada's prime minister\", \"on My First Million\"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context." + }, + "required": false, + "description": "What the user said about the name, in their words (\"the startup bank\", \"Canada's prime minister\", \"on My First Million\"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context.", + "name": "context", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } ], "responses": { @@ -20924,15 +7237,12 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WebhookSecretRotateResponse" + "$ref": "#/components/schemas/EntityResolveResponse" } } } @@ -20949,24 +7259,6 @@ "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. The route also answers 409 for a duplicate or a failed precondition, named in its description.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -20976,13 +7268,14 @@ } } }, - "/v1/monitors/{id}/trackers": { + "/v1/entities/{id}": { "get": { "tags": [ - "Monitors" + "Entities" ], - "operationId": "list_monitor_trackers", - "summary": "List monitor trackers", + "operationId": "get_entity", + "summary": "Get canonical entity metadata", + "description": "Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists.", "security": [ { "bearerAuth": [] @@ -20992,10 +7285,10 @@ { "schema": { "type": "string", - "description": "Monitor id." + "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." }, "required": true, - "description": "Monitor id.", + "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", "name": "id", "in": "path" } @@ -21023,7 +7316,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MonitorTrackersResponse" + "$ref": "#/components/schemas/EntityDetailResponse" } } } @@ -21034,6 +7327,9 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -21047,66 +7343,138 @@ "$ref": "#/components/responses/ServerError" } } - }, - "post": { + } + }, + "/v1/mentions": { + "get": { "tags": [ - "Monitors" + "Mentions" ], - "operationId": "add_monitor_trackers", - "summary": "Add trackers to monitor", - "description": "Attaches EXISTING trackers to the monitor by id ({ trackerIds: [\"trk_...\"] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count.", + "operationId": "list_mentions", + "summary": "Search mentions across media", + "description": "Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan.", "security": [ { "bearerAuth": [] } ], "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20 + }, + "required": false, + "name": "limit", + "in": "query" + }, { "schema": { "type": "string", - "description": "Monitor id." + "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." + }, + "required": false, + "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", + "name": "cursor", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "description": "The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." }, "required": true, - "description": "Monitor id.", - "name": "id", - "in": "path" + "description": "The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "entity_id", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." + }, + "required": false, + "description": "Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "channel_id", + "in": "query" }, { "schema": { "type": "string" }, "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "trackerIds": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - }, - "minItems": 1, - "maxItems": 90, - "description": "Ids of existing trackers (\"trk_...\") to attach to this monitor. Create trackers first via POST /v1/trackers. At most 90 IDs per request; duplicates count once. All IDs must belong to the account or none are attached." - } - }, - "required": [ - "trackerIds" - ], - "additionalProperties": false - } - } + "name": "q", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "positive", + "neutral", + "negative" + ] + }, + "required": false, + "name": "sentiment", + "in": "query" + }, + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "is_appearance", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "before", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "full" + ] + }, + "required": false, + "name": "details", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } - }, + ], "responses": { "200": { "description": "Success", @@ -21125,15 +7493,12 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MonitorAddTrackersResponse" + "$ref": "#/components/schemas/MentionListResponse" } } } @@ -21144,30 +7509,15 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -21177,103 +7527,131 @@ } } }, - "/v1/monitors/{id}/alerts": { + "/v1/recommendations": { "get": { "tags": [ - "Monitors" + "Recommendations" ], - "operationId": "list_monitor_alerts", - "summary": "List recent monitor alerts", - "description": "The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", + "operationId": "list_recommendations", + "summary": "Search recommendations across media", + "description": "Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds).", "security": [ { "bearerAuth": [] } ], "parameters": [ + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20 + }, + "required": false, + "name": "limit", + "in": "query" + }, { "schema": { "type": "string", - "description": "Monitor id." + "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor." + }, + "required": false, + "description": "Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.", + "name": "cursor", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "description": "The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." }, "required": true, - "description": "Monitor id.", - "name": "id", - "in": "path" + "description": "The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "entity_id", + "in": "query" }, { "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "description": "Alerts to return, newest first, 1 to 100. Default 25." + "type": "string", + "description": "Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." }, "required": false, - "description": "Alerts to return, newest first, 1 to 100. Default 25.", - "name": "limit", + "description": "Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "channel_id", "in": "query" - } - ], - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AlertListResponse" - } - } - } }, - "400": { - "$ref": "#/components/responses/InvalidRequest" + { + "schema": { + "type": "string", + "enum": [ + "sponsored", + "organic", + "mention" + ], + "description": "The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention)." + }, + "required": false, + "description": "The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).", + "name": "class", + "in": "query" }, - "401": { - "$ref": "#/components/responses/AuthenticationError" + { + "schema": { + "type": [ + "number", + "null" + ], + "minimum": 0, + "maximum": 1, + "default": 0.7 + }, + "required": false, + "name": "min_confidence", + "in": "query" }, - "403": { - "$ref": "#/components/responses/PermissionError" + { + "schema": { + "type": "string", + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "after", + "in": "query" }, - "404": { - "$ref": "#/components/responses/NotFound" + { + "schema": { + "type": "string", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "before", + "in": "query" }, - "429": { - "$ref": "#/components/responses/RateLimited" + { + "schema": { + "type": "boolean" + }, + "required": false, + "name": "include_disputed", + "in": "query" }, - "500": { - "$ref": "#/components/responses/ServerError" - } - } - } - }, - "/v1/trackers": { - "get": { - "tags": [ - "Trackers" - ], - "operationId": "list_trackers", - "summary": "List trackers", - "description": "All trackers for the account, newest first, with per-channel delivery counts for the current billing period. Single page, no pagination.", - "security": [ { - "bearerAuth": [] + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } ], "responses": { @@ -21299,7 +7677,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TrackerListResponse" + "$ref": "#/components/schemas/RecommendationListResponse" } } } @@ -21310,6 +7688,9 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -21323,20 +7704,55 @@ "$ref": "#/components/responses/ServerError" } } - }, + } + }, + "/v1/feedback": { "post": { "tags": [ - "Trackers" + "Feedback" ], - "operationId": "create_tracker", - "summary": "Create tracker", - "description": "Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId.", + "operationId": "submit_feedback", + "summary": "Submit feedback and corrections for a prior API query", + "description": "Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status \"logged\"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query.", "security": [ { "bearerAuth": [] } ], "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "recommendations", + "channel_sponsors", + "mentions", + "entities_search", + "entities", + "channels", + "monitor_alert", + "appearances", + "search", + "experience" + ], + "description": "Feedback type when omitted from the JSON body. Provide type in either location; the body takes precedence." + }, + "required": false, + "description": "Feedback type when omitted from the JSON body. Provide type in either location; the body takes precedence.", + "name": "type", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "description": "Query being reviewed when omitted from the JSON body. Body query fields override matching query-string fields." + }, + "required": false, + "description": "Query being reviewed when omitted from the JSON body. Body query fields override matching query-string fields.", + "name": "query", + "in": "query" + }, { "schema": { "type": "string" @@ -21345,7 +7761,7 @@ "name": "Idempotency-Key", "in": "header", "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." + "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." } ], "requestBody": { @@ -21354,70 +7770,175 @@ "schema": { "type": "object", "properties": { - "entityName": { - "type": "string", - "minLength": 1, - "description": "The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId." - }, - "entityType": { + "type": { "type": "string", "enum": [ - "person", - "organization", - "product", - "topic", - "channel" + "recommendations", + "channel_sponsors", + "mentions", + "entities_search", + "entities", + "channels", + "monitor_alert", + "appearances", + "search", + "experience" ], - "description": "Entity type of the tracked entity. Required on create." + "description": "The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional)." + }, + "query": { + "type": "object", + "additionalProperties": {}, + "description": "The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id." }, - "displayName": { + "endpoint": { "type": "string", - "description": "Optional label shown in alerts and the dashboard." - }, - "notifyEmail": { - "type": "boolean", - "description": "Per-tracker email delivery. Default true." + "maxLength": 500 }, - "notifyWebhook": { - "type": "boolean", - "description": "Per-tracker webhook delivery override. Paid plans only." + "method": { + "type": "string", + "enum": [ + "GET", + "POST", + "PATCH", + "PUT", + "DELETE" + ] }, - "notifySlack": { - "type": "boolean", - "description": "Per-tracker Slack delivery override. Paid plans only." + "request_id": { + "type": "string", + "maxLength": 200 }, - "webhookUrl": { + "result_url": { "type": "string", - "description": "Per-tracker webhook destination override (http/https)." + "maxLength": 2000 }, - "slackChannelId": { + "source_url": { "type": "string", - "description": "Per-tracker Slack channel override." + "maxLength": 2000 }, - "slackIntegrationId": { + "notes": { "type": "string", - "description": "Per-tracker Slack integration override." + "maxLength": 4000, + "description": "Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing." + }, + "corrections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "description": "Public id of the row being corrected, from the response you received: men_* (mentions, appearances), com_* (recommendations), ent_* (entities, sponsors), or the alert row id (monitor_alert). Omit for missed_alert and missing_result corrections, which have no row to target." + }, + "class": { + "type": "string", + "enum": [ + "sponsored", + "organic", + "mention" + ], + "description": "On recommendations feedback, the class the row should carry. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention)." + }, + "reason": { + "type": "string", + "enum": [ + "false_positive_ad_read", + "false_positive_endorsement", + "missed_ad_read", + "missed_endorsement", + "wrong_classification", + "wrong_entity", + "other" + ] + }, + "issue_type": { + "type": "string", + "enum": [ + "wrong_entity_type", + "wrong_entity", + "duplicate_entity", + "merge_suggestion", + "missing_result", + "stale_metadata", + "wrong_classification", + "bad_ranking", + "false_positive_alert", + "wrong_media", + "wrong_timestamp", + "duplicate_alert", + "missed_alert", + "delivery_issue", + "person_not_present", + "wrong_person", + "wrong_appearance_role", + "other" + ], + "description": "Issue classification for the correction. Entity-family values: wrong_entity_type (right entity, wrong type), wrong_entity (the row points at the wrong canonical entity), duplicate_entity (results split across variants of the same entity), merge_suggestion (propose the canonical merge for split variants), missing_result (a result you know should exist is absent), stale_metadata (name/website/channel metadata is outdated), wrong_classification (class-level error on a commercial row), bad_ranking (duplicates or aliases ranking above the canonical entity). monitor_alert values: false_positive_alert (the alert should not have fired), wrong_media (fired against the wrong video), wrong_timestamp (fired at the wrong position in the video), duplicate_alert (the same occurrence fired more than once), missed_alert (an expectation: an alert that should have fired but did not; no row to target), delivery_issue (the delivery itself was wrong: wrong channel, not received). appearances values: person_not_present (the person does not appear in the media), wrong_person (the appearance is attributed to the wrong person), wrong_appearance_role (right person, wrong role, e.g. guest vs host). other (escape hatch; detail in notes)." + }, + "suggested_change": { + "anyOf": [ + { + "$ref": "#/components/schemas/MergeSuggestionChange" + }, + { + "$ref": "#/components/schemas/WrongEntityChange" + }, + { + "$ref": "#/components/schemas/WrongEntityTypeChange" + }, + { + "$ref": "#/components/schemas/MissingResultChange" + }, + { + "$ref": "#/components/schemas/WrongClassificationChange" + }, + { + "$ref": "#/components/schemas/StaleMetadataChange" + }, + { + "$ref": "#/components/schemas/BadRankingChange" + }, + { + "$ref": "#/components/schemas/MissedAlertChange" + }, + { + "$ref": "#/components/schemas/DeliveryIssueChange" + }, + { + "$ref": "#/components/schemas/FreeformSuggestedChange" + } + ], + "description": "Your concrete proposed fix, shaped by issue_type: merge_suggestion → MergeSuggestionChange, wrong_entity/wrong_person → WrongEntityChange, wrong_entity_type → WrongEntityTypeChange, missing_result → MissingResultChange, wrong_classification → WrongClassificationChange, stale_metadata → StaleMetadataChange, bad_ranking → BadRankingChange, missed_alert → MissedAlertChange, delivery_issue → DeliveryIssueChange. Unknown keys are accepted and logged verbatim; only non-object values are rejected. Omit suggested_change entirely when you do not have a concrete fix." + }, + "notes": { + "type": "string", + "maxLength": 2000 + } + } + }, + "maxItems": 100, + "description": "Per-row corrections. Not allowed when type is experience." }, - "personMatchMode": { + "category": { "type": "string", "enum": [ - "mentions", - "appearances", - "both" + "wrong_entity", + "bad_data", + "missing", + "slow", + "confusing", + "other" ], - "description": "Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill." + "description": "What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience." }, - "filters": { - "type": "object", - "additionalProperties": {}, - "description": "Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching." + "mcp_call_id": { + "type": "string", + "pattern": "^mcpc_[0-9a-f]{32}$", + "description": "The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics." } - }, - "required": [ - "entityName", - "entityType" - ], - "additionalProperties": false + } } } } @@ -21448,37 +7969,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TrackerMutationResponse" - } - } - } - }, - "201": { - "description": "Tracker created", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TrackerMutationResponse" + "$ref": "#/components/schemas/FeedbackResponse" } } } @@ -21496,7 +7987,7 @@ "$ref": "#/components/responses/NotFound" }, "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. The route also answers 409 for a duplicate or a failed precondition, named in its description.", + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -21522,99 +8013,31 @@ } } }, - "/v1/trackers/{id}": { - "patch": { + "/v1/feedback/{feedback_id}": { + "get": { "tags": [ - "Trackers" + "Feedback" ], - "operationId": "update_tracker", - "summary": "Update tracker", - "description": "Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity.", + "operationId": "get_feedback", + "summary": "Read back a feedback submission and its review status", + "description": "Returns the submission (id, type, query, notes, created_at) plus its per-correction rows, each with a review status in the public vocabulary: pending_review, needs_information, accepted, accepted_with_changes, rejected, withdrawn, applied, reverted (accepted means a reviewer agreed; applied means the change is live in the index). Only the submitting user's keys can read a submission; unknown ids and other users' submissions both return 404 (never 403).", "security": [ { "bearerAuth": [] } - ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "Tracker id, trk_ form." - }, - "required": true, - "description": "Tracker id, trk_ form.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "displayName": { - "type": "string", - "description": "Optional label shown in alerts and the dashboard." - }, - "notifyEmail": { - "type": "boolean", - "description": "Per-tracker email delivery. Default true." - }, - "notifyWebhook": { - "type": "boolean", - "description": "Per-tracker webhook delivery override. Paid plans only." - }, - "notifySlack": { - "type": "boolean", - "description": "Per-tracker Slack delivery override. Paid plans only." - }, - "webhookUrl": { - "type": "string", - "description": "Per-tracker webhook destination override (http/https)." - }, - "slackChannelId": { - "type": "string", - "description": "Per-tracker Slack channel override." - }, - "slackIntegrationId": { - "type": "string", - "description": "Per-tracker Slack integration override." - }, - "personMatchMode": { - "type": "string", - "enum": [ - "mentions", - "appearances", - "both" - ], - "description": "Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill." - }, - "filters": { - "type": "object", - "additionalProperties": {}, - "description": "Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching." - }, - "paused": { - "type": "boolean", - "description": "Pause or resume the tracker. Paused trackers stop producing alerts; there is no backfill for the paused window." - } - }, - "additionalProperties": false - } - } - } - }, + ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "The feedback submission id POST /v1/feedback returned, fbk_ and digits." + }, + "required": true, + "description": "The feedback submission id POST /v1/feedback returned, fbk_ and digits.", + "name": "feedback_id", + "in": "path" + } + ], "responses": { "200": { "description": "Success", @@ -21633,15 +8056,12 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TrackerMutationResponse" + "$ref": "#/components/schemas/FeedbackReadbackResponse" } } } @@ -21658,24 +8078,6 @@ "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -21683,14 +8085,16 @@ "$ref": "#/components/responses/ServerError" } } - }, - "delete": { + } + }, + "/v1/channels/{channel_id}/sponsors": { + "get": { "tags": [ - "Trackers" + "Recommendations" ], - "operationId": "delete_tracker", - "summary": "Delete tracker", - "description": "Deletes the tracker. Cannot be undone.", + "operationId": "list_channel_sponsors", + "summary": "List recurring channel sponsors", + "description": "Rollup of recurring sponsors for a YouTube channel, ordered by ad read count. On a Pro+ plan the full list is served and min_ad_reads (default 3), status, and limit apply. Every other plan receives the free slice an anonymous visitor sees on arcmira.com, with meta.total naming the true count and access naming the gate; passing min_ad_reads, status, or limit on such a plan is refused with filter_requires_paid. Pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] @@ -21700,22 +8104,65 @@ { "schema": { "type": "string", - "description": "Tracker id, trk_ form." + "description": "YouTube channel id, the UC... form." }, "required": true, - "description": "Tracker id, trk_ form.", - "name": "id", + "description": "YouTube channel id, the UC... form.", + "name": "channel_id", "in": "path" }, { "schema": { - "type": "string" + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid." }, "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." + "description": "Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid.", + "name": "min_ad_reads", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "active", + "lapsed", + "ended", + "uncertain" + ], + "description": "Filter against the curated known-advertisers dataset. Pro+ only." + }, + "required": false, + "description": "Filter against the curated known-advertisers dataset. Pro+ only.", + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 200, + "description": "Sponsors to return. Default 100. Pro+ only; other plans receive the free slice." + }, + "required": false, + "description": "Sponsors to return. Default 100. Pro+ only; other plans receive the free slice.", + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } ], "responses": { @@ -21736,15 +8183,12 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/MessageResponse" + "$ref": "#/components/schemas/ChannelSponsorsResponse" } } } @@ -21755,30 +8199,15 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, "404": { "$ref": "#/components/responses/NotFound" }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -21788,14 +8217,14 @@ } } }, - "/v1/trackers/{id}/alerts": { + "/v1/search": { "get": { "tags": [ - "Trackers" + "Search" ], - "operationId": "list_tracker_alerts", - "summary": "List recent tracker alerts", - "description": "The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", + "operationId": "search", + "summary": "Retrieve spoken transcript slices for one topic", + "description": "Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] @@ -21805,23 +8234,133 @@ { "schema": { "type": "string", - "description": "Tracker id, trk_ form." + "minLength": 2, + "description": "One topic or phrase. Do not concatenate unrelated names; make one call per topic." }, "required": true, - "description": "Tracker id, trk_ form.", - "name": "id", - "in": "path" + "description": "One topic or phrase. Do not concatenate unrelated names; make one call per topic.", + "name": "q", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." + }, + "required": false, + "description": "Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "channel_ids", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Alias of channel_ids for code-mode clients; the union of both is the scope." + }, + "required": false, + "description": "Alias of channel_ids for code-mode clients; the union of both is the scope.", + "name": "channel", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." + }, + "required": false, + "description": "Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "entity_ids", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." + }, + "required": false, + "description": "Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "about", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve." + }, + "required": false, + "description": "Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.", + "name": "by", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk." + }, + "required": false, + "description": "Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk.", + "name": "kind", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "before", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "arcmira_premium", + "creator_captions", + "third_party_quick" + ], + "description": "Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid." + }, + "required": false, + "description": "Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid.", + "name": "source", + "in": "query" }, { "schema": { "type": "integer", "minimum": 1, - "maximum": 100, - "description": "Alerts to return, newest first, 1 to 100. Default 25." + "maximum": 20, + "default": 5, + "description": "Chunks to return, 1 to 20. Default 5." + }, + "required": false, + "description": "Chunks to return, 1 to 20. Default 5.", + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." }, "required": false, - "description": "Alerts to return, newest first, 1 to 100. Default 25.", - "name": "limit", + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", "in": "query" } ], @@ -21848,7 +8387,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AlertListResponse" + "$ref": "#/components/schemas/TranscriptSearchResponse" } } } @@ -21859,6 +8398,9 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -21870,23 +8412,69 @@ }, "500": { "$ref": "#/components/responses/ServerError" + }, + "503": { + "description": "search_unavailable. Search is unavailable right now. Retry-After carries the wait; nothing was charged.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } }, - "/v1/team/members": { + "/v1/entities/{id}/momentum": { "get": { "tags": [ - "Team" + "Entities" ], - "operationId": "list_team_members", - "summary": "List team members", - "description": "Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required).", + "operationId": "get_entity_momentum", + "summary": "Spoken-web heat for one entity", + "description": "Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] } ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect." + }, + "required": true, + "description": "Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.", + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" + } + ], "responses": { "200": { "description": "Success", @@ -21910,7 +8498,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TeamMembersResponse" + "$ref": "#/components/schemas/EntityMomentumResponse" } } } @@ -21921,6 +8509,9 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -21936,19 +8527,44 @@ } } }, - "/v1/team/spend": { + "/v1/channels/{channel_id}/coverage": { "get": { "tags": [ - "Team" + "Channels" ], - "operationId": "get_team_spend", - "summary": "Per-member spend and rows used this period", - "description": "Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required).", + "operationId": "get_channel_coverage", + "summary": "What the index holds for a channel", + "description": "How many videos of a YouTube channel are searchable, the newest publish date among them, and the split by transcript source class. Call it when a search or mention lookup came back empty, before telling anyone we do not cover a show, and cite indexed_through as the as-of date for mentions and search_indexed_through for transcript search. It cannot request indexing; channel backfill is not available yet. Free (0 rows). Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] } ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "YouTube channel id, the UC... form." + }, + "required": true, + "description": "YouTube channel id, the UC... form.", + "name": "channel_id", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" + } + ], "responses": { "200": { "description": "Success", @@ -21972,7 +8588,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TeamSpendResponse" + "$ref": "#/components/schemas/ChannelCoverageResponse" } } } @@ -21998,54 +8614,84 @@ } } }, - "/v1/team/usage-events": { + "/v1/channels/{channel_id}/videos": { "get": { "tags": [ - "Team" + "Channels" ], - "operationId": "list_team_usage_events", - "summary": "List team usage events", - "description": "Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required).", + "operationId": "list_channel_videos", + "summary": "Newest indexed videos of a channel", + "description": "The indexed videos of a YouTube channel, newest first, each with its video_id, title, publish date, duration, view count, and watch_url on arcmira.com. Pass next_cursor as cursor to continue. The signed token binds the route, filters, caller and visibility; invalid or old tokens return invalid_cursor. A first-page media ID fence excludes later insertions, including old-date backfills; edits and deletions to existing rows remain live. Call it for the latest or most recent episode of a show, or to list what a show published in a window, then pass a video_id to GET /v1/mentions/counts video_ids for what that episode mentions or to GET /v1/transcripts/{video_id} to read it. indexed_through is the newest date we hold for the channel. An empty list means nothing is indexed; channel backfill is not available yet. Bills one row per video returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] } ], "parameters": [ + { + "schema": { + "type": "string", + "description": "YouTube channel id, the UC... form." + }, + "required": true, + "description": "YouTube channel id, the UC... form.", + "name": "channel_id", + "in": "path" + }, { "schema": { "type": "integer", "minimum": 1, - "maximum": 100, - "default": 50, - "description": "Events per page, 1 to 100." + "maximum": 25, + "default": 10, + "description": "Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode." }, "required": false, - "description": "Events per page, 1 to 100.", + "description": "Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode.", "name": "limit", "in": "query" }, { "schema": { "type": "string", - "description": "Opaque cursor from a previous page's next_cursor." + "description": "Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor." }, "required": false, - "description": "Opaque cursor from a previous page's next_cursor.", + "description": "Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor.", "name": "cursor", "in": "query" }, { "schema": { - "type": "integer", - "minimum": 1, - "maximum": 90, - "default": 30, - "description": "Look-back window in days, bounded at 90." + "type": "string", + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." + }, + "required": false, + "description": "Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." }, "required": false, - "description": "Look-back window in days, bounded at 90.", - "name": "days", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "before", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", "in": "query" } ], @@ -22072,7 +8718,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TeamUsageEventsResponse" + "$ref": "#/components/schemas/ChannelVideosResponse" } } } @@ -22083,6 +8729,9 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -22098,14 +8747,14 @@ } } }, - "/v1/transcripts/{video_id}": { + "/v1/mentions/counts": { "get": { "tags": [ - "Transcripts" + "Mentions" ], - "operationId": "get_transcript", - "summary": "Get a video transcript", - "description": "Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections.", + "operationId": "count_mentions", + "summary": "Ranked catalog counts of who a set of channels mention", + "description": "A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.", "security": [ { "bearerAuth": [] @@ -22115,83 +8764,90 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them." }, - "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", - "in": "path" + "required": false, + "description": "Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them.", + "name": "channel_ids", + "in": "query" }, { "schema": { "type": "string", - "enum": [ - "captions", - "premium" - ], - "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings." + "description": "Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities." }, "required": false, - "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings.", - "name": "quality", + "description": "Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities.", + "name": "entity_ids", "in": "query" }, { "schema": { "type": "string", - "description": "Comma-separated caption language priority list, at most 5, tried in order (e.g. \"de,en\"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers." + "description": "Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos." }, "required": false, - "description": "Comma-separated caption language priority list, at most 5, tried in order (e.g. \"de,en\"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers.", - "name": "language", + "description": "Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos.", + "name": "video_ids", "in": "query" }, { "schema": { - "type": "boolean", - "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true." + "type": "string", + "description": "Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate." }, "required": false, - "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true.", - "name": "timestamps", + "description": "Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate.", + "name": "entity_types", "in": "query" }, { "schema": { - "type": [ - "number", - "null" + "type": "string", + "enum": [ + "mentions", + "appearances", + "both" ], - "minimum": 0, - "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge." + "default": "mentions", + "description": "mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions." }, "required": false, - "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge.", - "name": "start", + "description": "mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions.", + "name": "mode", "in": "query" }, { "schema": { - "type": [ - "number", - "null" - ], - "minimum": 0, - "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together." + "type": "string", + "description": "Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." }, "required": false, - "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together.", - "name": "end", + "description": "Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "after", "in": "query" }, { "schema": { - "type": "boolean", - "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query." + "type": "string", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive." }, "required": false, - "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query.", - "name": "refresh", + "description": "Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.", + "name": "before", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 40, + "default": 20, + "description": "Rows in the ranked table, 1 to 40. Default 20." + }, + "required": false, + "description": "Rows in the ranked table, 1 to 40. Default 20.", + "name": "limit", "in": "query" }, { @@ -22210,7 +8866,7 @@ ], "responses": { "200": { - "description": "state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state preparation_required: Premium is not owned yet; the quote and the POST that prepares it.", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -22231,27 +8887,51 @@ "content": { "application/json": { "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/TranscriptResponse" - }, - { - "$ref": "#/components/schemas/TranscriptPreparationRequired" - } - ], - "discriminator": { - "propertyName": "state", - "mapping": { - "ready": "#/components/schemas/TranscriptResponse", - "preparation_required": "#/components/schemas/TranscriptPreparationRequired" - } - } + "$ref": "#/components/schemas/MentionCountsResponse" } } } }, - "202": { - "description": "state pending: the Premium purchase is in flight; job carries its status and poll interval. No transcript content or charge.", + "400": { + "$ref": "#/components/responses/InvalidRequest" + }, + "401": { + "$ref": "#/components/responses/AuthenticationError" + }, + "402": { + "$ref": "#/components/responses/QuotaExceeded" + }, + "403": { + "$ref": "#/components/responses/PermissionError" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "429": { + "$ref": "#/components/responses/RateLimited" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/v1/monitors": { + "get": { + "tags": [ + "Monitors" + ], + "operationId": "list_monitors", + "summary": "List monitors", + "description": "All monitors for the account with tracker counts, alert counts for the current calendar month, and Slack display metadata. Single page, no pagination.", + "security": [ + { + "bearerAuth": [] + } + ], + "responses": { + "200": { + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -22272,7 +8952,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptPending" + "$ref": "#/components/schemas/MonitorListResponse" } } } @@ -22283,9 +8963,6 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, - "402": { - "$ref": "#/components/responses/QuotaExceeded" - }, "403": { "$ref": "#/components/responses/PermissionError" }, @@ -22297,39 +8974,16 @@ }, "500": { "$ref": "#/components/responses/ServerError" - }, - "503": { - "description": "transcript_fetching. The caption track is being fetched now. Retry-After and retry_after_seconds carry the wait; nothing was charged.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } } } - } - }, - "/v1/transcripts/{video_id}/quote": { - "get": { + }, + "post": { "tags": [ - "Transcripts" + "Monitors" ], - "operationId": "quote_transcription", - "summary": "Quote a whole-video Premium purchase", - "description": "Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id.", + "operationId": "create_monitor", + "summary": "Create monitor", + "description": "Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint.", "security": [ { "bearerAuth": [] @@ -22338,15 +8992,87 @@ "parameters": [ { "schema": { - "type": "string", - "description": "YouTube video id, 11 characters." + "type": "string" }, - "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", - "in": "path" + "required": false, + "name": "Idempotency-Key", + "in": "header", + "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "description": "Display name (1-100 characters). Required on create." + }, + "notify_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 20, + "description": "Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []." + }, + "notify_frequency": { + "type": "string", + "enum": [ + "realtime", + "hourly", + "daily" + ], + "description": "Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent." + }, + "digest_day": { + "type": "string", + "description": "Digest day of week. Default \"monday\". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies." + }, + "digest_time": { + "type": "string", + "description": "Digest send hour as HH:MM (account timezone). Default \"09:00\". Applies to daily digests." + }, + "notify_webhook": { + "type": "boolean", + "description": "Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter." + }, + "webhook_url": { + "type": "string", + "description": "Destination URL for webhook alert deliveries." + }, + "notify_slack": { + "type": "boolean", + "description": "Enable Slack delivery. Requires a Slack integration connected in the dashboard." + }, + "slack_integration_id": { + "type": "string", + "description": "Slack integration id from the dashboard OAuth flow." + }, + "slack_channel_id": { + "type": "string", + "description": "Slack channel id to deliver to." + }, + "team_id": { + "type": "string", + "minLength": 1, + "description": "Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only." + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + } + } + }, "responses": { "200": { "description": "Success", @@ -22365,12 +9091,45 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptPurchaseQuote" + "$ref": "#/components/schemas/MonitorMutationResponse" + } + } + } + }, + "201": { + "description": "Monitor created. When this request enabled webhook signing, monitor.webhook_secret is present and recoverable by retrying with the original Idempotency-Key during its recovery window.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimit-Limit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimit-Remaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/MonitorMutationResponse" } } } @@ -22387,6 +9146,24 @@ "404": { "$ref": "#/components/responses/NotFound" }, + "409": { + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -22396,14 +9173,14 @@ } } }, - "/v1/videos/{video_id}/captions": { - "get": { + "/v1/monitors/{id}": { + "patch": { "tags": [ - "Transcripts" + "Monitors" ], - "operationId": "get_video_captions", - "summary": "List the caption tracks a video offers", - "description": "Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background.", + "operationId": "update_monitor", + "summary": "Update monitor", + "description": "A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter.", "security": [ { "bearerAuth": [] @@ -22413,27 +9190,92 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Monitor id." }, "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", + "description": "Monitor id.", + "name": "id", "in": "path" }, { "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + "type": "string" }, "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" + "name": "Idempotency-Key", + "in": "header", + "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "description": "Display name (1-100 characters). Required on create." + }, + "notify_emails": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 20, + "description": "Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []." + }, + "notify_frequency": { + "type": "string", + "enum": [ + "realtime", + "hourly", + "daily" + ], + "description": "Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent." + }, + "digest_day": { + "type": "string", + "description": "Digest day of week. Default \"monday\". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies." + }, + "digest_time": { + "type": "string", + "description": "Digest send hour as HH:MM (account timezone). Default \"09:00\". Applies to daily digests." + }, + "notify_webhook": { + "type": "boolean", + "description": "Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter." + }, + "webhook_url": { + "type": "string", + "description": "Destination URL for webhook alert deliveries." + }, + "notify_slack": { + "type": "boolean", + "description": "Enable Slack delivery. Requires a Slack integration connected in the dashboard." + }, + "slack_integration_id": { + "type": "string", + "description": "Slack integration id from the dashboard OAuth flow." + }, + "slack_channel_id": { + "type": "string", + "description": "Slack channel id to deliver to." + }, + "paused": { + "type": "boolean", + "description": "Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued." + } + }, + "additionalProperties": false + } + } } - ], + }, "responses": { "200": { "description": "Success", @@ -22452,12 +9294,15 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VideoCaptionsResponse" + "$ref": "#/components/schemas/MonitorMutationResponse" } } } @@ -22474,23 +9319,14 @@ "404": { "$ref": "#/components/responses/NotFound" }, - "429": { - "$ref": "#/components/responses/RateLimited" - }, - "500": { - "$ref": "#/components/responses/ServerError" - }, - "503": { - "description": "transcript_fetching. The listing is cold and is being fetched now. Retry-After and retry_after_seconds carry the wait; this route never charges rows.", + "409": { + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" } }, "content": { @@ -22500,24 +9336,38 @@ } } } + }, + "429": { + "$ref": "#/components/responses/RateLimited" + }, + "500": { + "$ref": "#/components/responses/ServerError" } } - } - }, - "/v1/transcriptions": { - "post": { + }, + "delete": { "tags": [ - "Transcriptions" + "Monitors" ], - "operationId": "submit_transcription", - "summary": "Submit a YouTube video for transcription", - "description": "Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it.", + "operationId": "delete_monitor", + "summary": "Delete monitor", + "description": "Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again.", "security": [ { "bearerAuth": [] } ], "parameters": [ + { + "schema": { + "type": "string", + "description": "Monitor id." + }, + "required": true, + "description": "Monitor id.", + "name": "id", + "in": "path" + }, { "schema": { "type": "string" @@ -22526,87 +9376,12 @@ "name": "Idempotency-Key", "in": "header", "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "video_id": { - "type": "string", - "pattern": "^[A-Za-z0-9_-]{11}$", - "description": "YouTube video id (11 characters). Either video_id or url is required.", - "example": "dQw4w9WgXcQ" - }, - "videoId": { - "type": "string", - "pattern": "^[A-Za-z0-9_-]{11}$", - "description": "Alias of video_id for one release.", - "deprecated": true - }, - "url": { - "type": "string", - "format": "uri", - "description": "A YouTube watch/short/live URL. Either video_id or url is required." - }, - "max_rows": { - "type": "integer", - "minimum": 0, - "maximum": 3600, - "description": "Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0.", - "example": 300 - }, - "max_on_demand_cents": { - "type": "integer", - "minimum": 0, - "default": 0, - "description": "Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows." - } - }, - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "job.state is ready, failed or refunded: the transcript is servable, or the purchase ended without one.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TranscriptionSubmitResponse" - } - } - } - }, - "202": { - "description": "job.state is pending: generation is under way. Poll job.status_url after Retry-After.", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -22623,9 +9398,6 @@ "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" - }, "Idempotency-Replayed": { "$ref": "#/components/headers/Idempotency-Replayed" } @@ -22633,7 +9405,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptionSubmitResponse" + "$ref": "#/components/schemas/MonitorDeleteResponse" } } } @@ -22644,47 +9416,14 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, - "402": { - "description": "quota_exceeded or spend_limit_exceeded. Nothing was charged; quote carries the refused price.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } - }, "403": { - "description": "paid_plan_required. error.unlock names the plan that buys Premium; quote carries the price.", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Error" - } - } - } + "$ref": "#/components/responses/PermissionError" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. max_rows_exceeded, max_charge_exceeded and purchase_authority_changed carry quote; max_charge_exceeded on an already accepted purchase carries existing_request_id.", + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -22708,14 +9447,16 @@ "$ref": "#/components/responses/ServerError" } } - }, - "get": { + } + }, + "/v1/monitors/{id}/webhook-secret/rotate": { + "post": { "tags": [ - "Transcriptions" + "Monitors" ], - "operationId": "list_transcriptions", - "summary": "List your transcription requests", - "description": "Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`.", + "operationId": "rotate_monitor_webhook_secret", + "summary": "Rotate the monitor webhook signing secret", + "description": "Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope.", "security": [ { "bearerAuth": [] @@ -22725,49 +9466,22 @@ { "schema": { "type": "string", - "pattern": "^[A-Za-z0-9_-]{11}$", - "description": "Filter to your requests for one video." - }, - "required": false, - "description": "Filter to your requests for one video.", - "name": "video_id", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 20, - "description": "Requests per page, from 1 to 100. Default 20." - }, - "required": false, - "description": "Requests per page, from 1 to 100. Default 20.", - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "string", - "description": "Signed continuation from next_cursor. Keep the same filter, limit and credential." + "description": "Monitor id." }, - "required": false, - "description": "Signed continuation from next_cursor. Keep the same filter, limit and credential.", - "name": "cursor", - "in": "query" + "required": true, + "description": "Monitor id.", + "name": "id", + "in": "path" }, { "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + "type": "string" }, "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" + "name": "Idempotency-Key", + "in": "header", + "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], "responses": { @@ -22788,12 +9502,15 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptionListResponse" + "$ref": "#/components/schemas/WebhookSecretRotateResponse" } } } @@ -22810,6 +9527,24 @@ "404": { "$ref": "#/components/responses/NotFound" }, + "409": { + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. The route also answers 409 for a duplicate or a failed precondition, named in its description.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -22819,14 +9554,13 @@ } } }, - "/v1/transcriptions/{id}": { + "/v1/monitors/{id}/trackers": { "get": { "tags": [ - "Transcriptions" + "Monitors" ], - "operationId": "get_transcription", - "summary": "Poll transcription status", - "description": "Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up.", + "operationId": "list_monitor_trackers", + "summary": "List monitor trackers", "security": [ { "bearerAuth": [] @@ -22836,25 +9570,12 @@ { "schema": { "type": "string", - "description": "Transcription request id, the UUID POST /v1/transcriptions returned." - }, - "required": true, - "description": "Transcription request id, the UUID POST /v1/transcriptions returned.", - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "mcp-tool" - ], - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." - }, - "required": false, - "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", - "name": "src", - "in": "query" + "description": "Monitor id." + }, + "required": true, + "description": "Monitor id.", + "name": "id", + "in": "path" } ], "responses": { @@ -22875,15 +9596,12 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" - }, - "Retry-After": { - "$ref": "#/components/headers/Retry-After" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptionJob" + "$ref": "#/components/schemas/MonitorTrackersResponse" } } } @@ -22907,16 +9625,14 @@ "$ref": "#/components/responses/ServerError" } } - } - }, - "/v1/videos/{video_id}/corrections": { + }, "post": { "tags": [ - "Corrections" + "Monitors" ], - "operationId": "submit_correction", - "summary": "Submit a transcript correction", - "description": "Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key.", + "operationId": "add_monitor_trackers", + "summary": "Add trackers to monitor", + "description": "Attaches EXISTING trackers to the monitor by id ({ tracker_ids: [\"trk_...\"] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count.", "security": [ { "bearerAuth": [] @@ -22926,11 +9642,11 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Monitor id." }, "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", + "description": "Monitor id.", + "name": "id", "in": "path" }, { @@ -22941,7 +9657,7 @@ "name": "Idempotency-Key", "in": "header", "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once." + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], "requestBody": { @@ -22950,55 +9666,21 @@ "schema": { "type": "object", "properties": { - "kind": { - "type": "string", - "enum": [ - "line_edit", - "speaker_reassign", - "speaker_identify", - "add_person", - "entity_tag", - "segment_rewrite" - ] - }, - "seq": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value." - }, - "revision": { - "type": "string", - "description": "The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read." - }, - "anchor": { - "type": "object", - "properties": { - "segmentIndex": { - "type": "integer", - "minimum": 0 - }, - "contentHash": { - "type": "string", - "minLength": 1, - "description": "djb2 hash of the covered segment text (joined with \\n for ranges), computed against your projected view of the transcript." - } + "tracker_ids": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 }, - "required": [ - "segmentIndex", - "contentHash" - ], - "description": "Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite." - }, - "payload": { - "type": "object", - "additionalProperties": {}, - "description": "Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex." + "minItems": 1, + "maxItems": 90, + "description": "Ids of existing trackers (\"trk_...\") to attach to this monitor. Create trackers first via POST /v1/trackers. At most 90 IDs per request; duplicates count once. All IDs must belong to the account or none are attached." } }, "required": [ - "kind", - "payload" - ] + "tracker_ids" + ], + "additionalProperties": false } } } @@ -23029,37 +9711,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CorrectionAcceptedResponse" - } - } - } - }, - "201": { - "description": "Correction accepted ({ ok, kind, result }; result.id withdraws it later)", - "headers": { - "X-Request-Id": { - "$ref": "#/components/headers/X-Request-Id" - }, - "X-Arcmira-Version": { - "$ref": "#/components/headers/X-Arcmira-Version" - }, - "RateLimit-Limit": { - "$ref": "#/components/headers/RateLimit-Limit" - }, - "RateLimit-Remaining": { - "$ref": "#/components/headers/RateLimit-Remaining" - }, - "RateLimit-Reset": { - "$ref": "#/components/headers/RateLimit-Reset" - }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CorrectionAcceptedResponse" + "$ref": "#/components/schemas/MonitorAddTrackersResponse" } } } @@ -23077,7 +9729,7 @@ "$ref": "#/components/responses/NotFound" }, "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. The route also answers 409 for a duplicate or a failed precondition, named in its description.", + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23094,24 +9746,92 @@ } } }, - "412": { - "description": "sequence_mismatch. error.expected_seq is the next seq for this video. Refetch, rebase, resend under the same key.", + "429": { + "$ref": "#/components/responses/RateLimited" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/v1/monitors/{id}/alerts": { + "get": { + "tags": [ + "Monitors" + ], + "operationId": "list_monitor_alerts", + "summary": "List recent monitor alerts", + "description": "The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "schema": { + "type": "string", + "description": "Monitor id." + }, + "required": true, + "description": "Monitor id.", + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Alerts to return, newest first, 1 to 100. Default 25." + }, + "required": false, + "description": "Alerts to return, newest first, 1 to 100. Default 25.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" }, "X-Arcmira-Version": { "$ref": "#/components/headers/X-Arcmira-Version" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimit-Limit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimit-Remaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimit-Reset" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Error" + "$ref": "#/components/schemas/AlertListResponse" } } } }, + "400": { + "$ref": "#/components/responses/InvalidRequest" + }, + "401": { + "$ref": "#/components/responses/AuthenticationError" + }, + "403": { + "$ref": "#/components/responses/PermissionError" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -23121,13 +9841,14 @@ } } }, - "/v1/corrections/speaker-edits/{id}": { - "delete": { + "/v1/monitors/{id}/entities": { + "post": { "tags": [ - "Corrections" + "Monitors" ], - "operationId": "withdraw_speaker_edit", - "summary": "Withdraw your pending speaker reassign or split", + "operationId": "add_monitor_entities", + "summary": "Follow entities in a monitor by id", + "description": "Follows each entity ({ entity_ids: [\"ent_...\"] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes.", "security": [ { "bearerAuth": [] @@ -23137,17 +9858,61 @@ { "schema": { "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." + "description": "Monitor id." }, "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", + "description": "Monitor id.", "name": "id", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "Idempotency-Key", + "in": "header", + "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "entity_ids": { + "type": "array", + "items": { + "type": "string", + "pattern": "^ent_[1-9][0-9]*$" + }, + "minItems": 1, + "maxItems": 90, + "description": "Entity ids (\"ent_...\") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity." + }, + "person_match_mode": { + "type": "string", + "enum": [ + "mentions", + "appearances", + "both" + ], + "description": "For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id})." + } + }, + "required": [ + "entity_ids" + ], + "additionalProperties": false + } + } + } + }, "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23163,12 +9928,15 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/MonitorAddEntitiesResponse" } } } @@ -23185,6 +9953,24 @@ "404": { "$ref": "#/components/responses/NotFound" }, + "409": { + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -23194,33 +9980,22 @@ } } }, - "/v1/corrections/entity-tags/{id}": { - "delete": { + "/v1/integrations/slack": { + "get": { "tags": [ - "Corrections" + "Monitors" ], - "operationId": "withdraw_entity_tag", - "summary": "Withdraw your pending entity tag", + "operationId": "list_slack_integrations", + "summary": "List connected Slack workspaces", + "description": "The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination.", "security": [ { "bearerAuth": [] } ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." - }, - "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", - "name": "id", - "in": "path" - } - ], "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23241,7 +10016,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/SlackIntegrationListResponse" } } } @@ -23267,33 +10042,22 @@ } } }, - "/v1/corrections/segment-rewrites/{id}": { - "delete": { + "/v1/trackers": { + "get": { "tags": [ - "Corrections" + "Trackers" ], - "operationId": "withdraw_segment_rewrite", - "summary": "Withdraw your pending segment rewrite", + "operationId": "list_trackers", + "summary": "List trackers", + "description": "All trackers for the account, newest first, with per-channel delivery counts for the current billing period. Single page, no pagination.", "security": [ { "bearerAuth": [] } ], - "parameters": [ - { - "schema": { - "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." - }, - "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", - "name": "id", - "in": "path" - } - ], "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23314,7 +10078,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/TrackerListResponse" } } } @@ -23337,33 +10101,21 @@ "500": { "$ref": "#/components/responses/ServerError" } - } - } - }, - "/v1/transcripts/{video_id}/edits": { + } + }, "post": { "tags": [ - "Corrections" + "Trackers" ], - "operationId": "submit_transcript_edit", - "summary": "Submit a transcript line-text correction", - "description": "Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key.", + "operationId": "create_tracker", + "summary": "Create tracker", + "description": "Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id.", "security": [ { "bearerAuth": [] } ], "parameters": [ - { - "schema": { - "type": "string", - "description": "YouTube video id, 11 characters." - }, - "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", - "in": "path" - }, { "schema": { "type": "string" @@ -23372,7 +10124,7 @@ "name": "Idempotency-Key", "in": "header", "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], "requestBody": { @@ -23381,36 +10133,107 @@ "schema": { "type": "object", "properties": { - "segmentIndex": { - "type": "integer", - "minimum": 0 + "entity_name": { + "type": "string", + "minLength": 1, + "description": "The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id." }, - "originalText": { + "entity_type": { "type": "string", - "description": "The current segment text you are correcting (guards against applying to a changed segment)." + "enum": [ + "person", + "organization", + "product", + "topic", + "channel" + ], + "description": "Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization." }, - "correctedText": { + "display_name": { "type": "string", - "minLength": 1, - "maxLength": 2000 + "description": "Optional label shown in alerts and the dashboard." + }, + "notify_email": { + "type": "boolean", + "description": "Per-tracker email delivery. Default true." + }, + "notify_webhook": { + "type": "boolean", + "description": "Per-tracker webhook delivery override. Paid plans only." + }, + "notify_slack": { + "type": "boolean", + "description": "Per-tracker Slack delivery override. Paid plans only." + }, + "webhook_url": { + "type": "string", + "description": "Per-tracker webhook destination override (http/https)." + }, + "slack_channel_id": { + "type": "string", + "description": "Per-tracker Slack channel override." + }, + "slack_integration_id": { + "type": "string", + "description": "Per-tracker Slack integration override." }, - "revision": { + "person_match_mode": { "type": "string", - "description": "The revision from the Premium transcript read." + "enum": [ + "mentions", + "appearances", + "both" + ], + "description": "Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill." + }, + "filters": { + "type": "object", + "additionalProperties": {}, + "description": "Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching." } }, "required": [ - "segmentIndex", - "originalText", - "correctedText" - ] + "entity_name", + "entity_type" + ], + "additionalProperties": false } } } }, "responses": { + "200": { + "description": "Success", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimit-Limit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimit-Remaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TrackerMutationResponse" + } + } + } + }, "201": { - "description": "Edit accepted, pending review", + "description": "Tracker created", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23434,7 +10257,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptEditSubmittedResponse" + "$ref": "#/components/schemas/TrackerMutationResponse" } } } @@ -23478,13 +10301,14 @@ } } }, - "/v1/transcripts/{video_id}/edits/{id}": { - "delete": { + "/v1/trackers/{id}": { + "patch": { "tags": [ - "Corrections" + "Trackers" ], - "operationId": "withdraw_transcript_edit", - "summary": "Withdraw your pending line edit", + "operationId": "update_tracker", + "summary": "Update tracker", + "description": "Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity.", "security": [ { "bearerAuth": [] @@ -23494,27 +10318,85 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Tracker id, trk_ form." }, "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", + "description": "Tracker id, trk_ form.", + "name": "id", "in": "path" }, { "schema": { - "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." + "type": "string" }, - "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", - "name": "id", - "in": "path" + "required": false, + "name": "Idempotency-Key", + "in": "header", + "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "description": "Optional label shown in alerts and the dashboard." + }, + "notify_email": { + "type": "boolean", + "description": "Per-tracker email delivery. Default true." + }, + "notify_webhook": { + "type": "boolean", + "description": "Per-tracker webhook delivery override. Paid plans only." + }, + "notify_slack": { + "type": "boolean", + "description": "Per-tracker Slack delivery override. Paid plans only." + }, + "webhook_url": { + "type": "string", + "description": "Per-tracker webhook destination override (http/https)." + }, + "slack_channel_id": { + "type": "string", + "description": "Per-tracker Slack channel override." + }, + "slack_integration_id": { + "type": "string", + "description": "Per-tracker Slack integration override." + }, + "person_match_mode": { + "type": "string", + "enum": [ + "mentions", + "appearances", + "both" + ], + "description": "Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill." + }, + "filters": { + "type": "object", + "additionalProperties": {}, + "description": "Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching." + }, + "paused": { + "type": "boolean", + "description": "Pause or resume the tracker. Paused trackers stop producing alerts; there is no backfill for the paused window." + } + }, + "additionalProperties": false + } + } + } + }, "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23530,12 +10412,15 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + }, + "Idempotency-Replayed": { + "$ref": "#/components/headers/Idempotency-Replayed" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/TrackerMutationResponse" } } } @@ -23552,6 +10437,24 @@ "404": { "$ref": "#/components/responses/NotFound" }, + "409": { + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, "429": { "$ref": "#/components/responses/RateLimited" }, @@ -23559,16 +10462,14 @@ "$ref": "#/components/responses/ServerError" } } - } - }, - "/v1/transcripts/{video_id}/speakers": { - "post": { + }, + "delete": { "tags": [ - "Corrections" + "Trackers" ], - "operationId": "identify_transcript_speaker", - "summary": "Identify a diarized speaker as a person", - "description": "Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows).", + "operationId": "delete_tracker", + "summary": "Delete tracker", + "description": "Deletes the tracker. Cannot be undone.", "security": [ { "bearerAuth": [] @@ -23578,11 +10479,11 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Tracker id, trk_ form." }, "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", + "description": "Tracker id, trk_ form.", + "name": "id", "in": "path" }, { @@ -23593,46 +10494,12 @@ "name": "Idempotency-Key", "in": "header", "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." + "description": "One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation." } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "speakerId": { - "type": "integer", - "minimum": 0, - "description": "A speakers[].id from the Premium transcript read that revision names." - }, - "entityId": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "Existing person entity id. Either entityId or name is required." - }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 120, - "description": "Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required." - }, - "revision": { - "type": "string", - "description": "The revision of the transcript read speakerId came from. Required." - } - }, - "required": [ - "speakerId" - ] - } - } - } - }, "responses": { - "201": { - "description": "Identification accepted, pending review", + "200": { + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23656,7 +10523,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SpeakerIdentificationSubmittedResponse" + "$ref": "#/components/schemas/MessageResponse" } } } @@ -23674,7 +10541,7 @@ "$ref": "#/components/responses/NotFound" }, "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent. The route also answers 409 for a duplicate or a failed precondition, named in its description.", + "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23700,14 +10567,14 @@ } } }, - "/v1/transcripts/{video_id}/speakers/{id}": { - "delete": { + "/v1/trackers/{id}/alerts": { + "get": { "tags": [ - "Corrections" + "Trackers" ], - "operationId": "withdraw_speaker_identification", - "summary": "Withdraw your pending speaker identification", - "description": "Withdrawing also removes the community-attributed appearance the identification created.", + "operationId": "list_tracker_alerts", + "summary": "List recent tracker alerts", + "description": "The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", "security": [ { "bearerAuth": [] @@ -23717,27 +10584,29 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "description": "Tracker id, trk_ form." }, "required": true, - "description": "YouTube video id, 11 characters.", - "name": "video_id", + "description": "Tracker id, trk_ form.", + "name": "id", "in": "path" }, { "schema": { - "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." - }, - "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", - "name": "id", - "in": "path" + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Alerts to return, newest first, 1 to 100. Default 25." + }, + "required": false, + "description": "Alerts to return, newest first, 1 to 100. Default 25.", + "name": "limit", + "in": "query" } ], "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23758,7 +10627,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/AlertListResponse" } } } @@ -23784,14 +10653,14 @@ } } }, - "/v1/transcripts/{video_id}/merges": { - "post": { + "/v1/transcripts/{video_id}": { + "get": { "tags": [ - "Corrections" + "Transcripts" ], - "operationId": "submit_video_merge", - "summary": "Submit a video-level entity merge", - "description": "Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows).", + "operationId": "get_transcript", + "summary": "Get a video transcript", + "description": "Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections.", "security": [ { "bearerAuth": [] @@ -23810,52 +10679,93 @@ }, { "schema": { - "type": "string" + "type": "string", + "enum": [ + "captions", + "premium" + ], + "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings." }, "required": false, - "name": "Idempotency-Key", - "in": "header", - "example": "8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - "description": "1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced." + "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.", + "name": "quality", + "in": "query" + }, + { + "schema": { + "type": "string", + "description": "Comma-separated caption language priority list, at most 5, tried in order (e.g. \"de,en\"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers." + }, + "required": false, + "description": "Comma-separated caption language priority list, at most 5, tried in order (e.g. \"de,en\"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers.", + "name": "language", + "in": "query" + }, + { + "schema": { + "type": "boolean", + "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true." + }, + "required": false, + "description": "false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true.", + "name": "timestamps", + "in": "query" + }, + { + "schema": { + "type": [ + "number", + "null" + ], + "minimum": 0, + "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge." + }, + "required": false, + "description": "Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge.", + "name": "start", + "in": "query" + }, + { + "schema": { + "type": [ + "number", + "null" + ], + "minimum": 0, + "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together." + }, + "required": false, + "description": "Window end in seconds, greater than start and no greater than the video duration. Send start and end together.", + "name": "end", + "in": "query" + }, + { + "schema": { + "type": "boolean", + "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query." + }, + "required": false, + "description": "Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query.", + "name": "refresh", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "sourceName": { - "type": "string", - "minLength": 1, - "maxLength": 120, - "description": "The name as it appears in this video (e.g. a first-name-only mention)." - }, - "targetEntityId": { - "type": "integer", - "exclusiveMinimum": 0, - "description": "The canonical entity these mentions actually refer to." - }, - "replaceWith": { - "type": "string", - "maxLength": 120, - "description": "Optional respelling applied to the transcript text (e.g. \"Imad\" → \"Emad\")." - }, - "revision": { - "type": "string" - } - }, - "required": [ - "sourceName", - "targetEntityId" - ] - } - } - } - }, "responses": { - "201": { - "description": "Merge accepted, pending review", + "200": { + "description": "state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note).", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23871,15 +10781,39 @@ }, "RateLimit-Reset": { "$ref": "#/components/headers/RateLimit-Reset" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TranscriptResponse" + } + } + } + }, + "202": { + "description": "state pending: the Premium purchase this read started or joined is in flight; job carries its status and poll interval. No transcript content yet.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" }, - "Idempotency-Replayed": { - "$ref": "#/components/headers/Idempotency-Replayed" + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "RateLimit-Limit": { + "$ref": "#/components/headers/RateLimit-Limit" + }, + "RateLimit-Remaining": { + "$ref": "#/components/headers/RateLimit-Remaining" + }, + "RateLimit-Reset": { + "$ref": "#/components/headers/RateLimit-Reset" } }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VideoMergeSubmittedResponse" + "$ref": "#/components/schemas/TranscriptPending" } } } @@ -23890,14 +10824,26 @@ "401": { "$ref": "#/components/responses/AuthenticationError" }, - "403": { - "$ref": "#/components/responses/PermissionError" - }, - "404": { - "$ref": "#/components/responses/NotFound" + "402": { + "description": "quota_exceeded or spend_limit_exceeded. The plan allows Premium but the included credits and the on-demand budget do not cover this video; quote carries the refused price and nothing was charged.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } }, - "409": { - "description": "Conflict. idempotency_conflict means the Idempotency-Key was finalized with a different request intent.", + "403": { + "description": "paid_plan_required. error.unlock names the plan that buys Premium; quote carries the price.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -23914,20 +10860,47 @@ } } }, + "404": { + "$ref": "#/components/responses/NotFound" + }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/ServerError" + }, + "503": { + "description": "transcript_fetching. The caption track is being fetched now. Retry-After and retry_after_seconds carry the wait; nothing was charged.", + "headers": { + "X-Request-Id": { + "$ref": "#/components/headers/X-Request-Id" + }, + "X-Arcmira-Version": { + "$ref": "#/components/headers/X-Arcmira-Version" + }, + "Retry-After": { + "$ref": "#/components/headers/Retry-After" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } - }, + } + }, + "/v1/transcripts/{video_id}/quote": { "get": { "tags": [ - "Corrections" + "Transcripts" ], - "operationId": "list_video_merges", - "summary": "List your pending video merges", + "operationId": "quote_transcription", + "summary": "Quote a whole-video Premium purchase", + "description": "Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id.", "security": [ { "bearerAuth": [] @@ -23968,7 +10941,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VideoMergeListResponse" + "$ref": "#/components/schemas/TranscriptPurchaseQuote" } } } @@ -23994,13 +10967,14 @@ } } }, - "/v1/transcripts/{video_id}/merges/{id}": { - "delete": { + "/v1/transcriptions": { + "get": { "tags": [ - "Corrections" + "Transcriptions" ], - "operationId": "withdraw_video_merge", - "summary": "Withdraw your pending video merge", + "operationId": "list_transcriptions", + "summary": "List your transcription requests", + "description": "Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`.", "security": [ { "bearerAuth": [] @@ -24010,27 +10984,54 @@ { "schema": { "type": "string", - "description": "YouTube video id, 11 characters." + "pattern": "^[A-Za-z0-9_-]{11}$", + "description": "Filter to your requests for one video." }, - "required": true, - "description": "YouTube video id, 11 characters.", + "required": false, + "description": "Filter to your requests for one video.", "name": "video_id", - "in": "path" + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 20, + "description": "Requests per page, from 1 to 100. Default 20." + }, + "required": false, + "description": "Requests per page, from 1 to 100. Default 20.", + "name": "limit", + "in": "query" }, { "schema": { "type": "string", - "description": "The pending row id, as result.id of the response that accepted it." + "description": "Signed continuation from next_cursor. Keep the same filter, limit and credential." }, - "required": true, - "description": "The pending row id, as result.id of the response that accepted it.", - "name": "id", - "in": "path" + "required": false, + "description": "Signed continuation from next_cursor. Keep the same filter, limit and credential.", + "name": "cursor", + "in": "query" + }, + { + "schema": { + "type": "string", + "enum": [ + "mcp-tool" + ], + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client." + }, + "required": false, + "description": "The surface making this call. The Arcmira MCP server sends mcp-tool so every unlock link in a gate attributes to the directory install. Omit from your own client.", + "name": "src", + "in": "query" } ], "responses": { "200": { - "description": "Withdrawn", + "description": "Success", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -24051,7 +11052,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/WithdrawnResponse" + "$ref": "#/components/schemas/TranscriptionListResponse" } } } diff --git a/llms.txt b/llms.txt index 3f49e97..3127c0a 100644 --- a/llms.txt +++ b/llms.txt @@ -12,30 +12,52 @@ pip install arcmira Set `ARCMIRA_API_KEY` or pass `api_key` to the client. -## Premium transcript quickstart +## Ids first + +Reads take ids, never names. Resolve a name, then read with the id. ```python from arcmira import Arcmira -transcript = Arcmira().transcripts.prepare_and_wait("dQw4w9WgXcQ") -print(transcript.lines) +client = Arcmira() +match = client.entities.resolve(q="Ramp", type="organization", context="the corporate card") +ramp = match.best or match.suggested +for mention in client.mentions.list(entity_id=ramp.id, after="2026-09-01", before="2026-10-01"): + print(mention.media.video_id, mention.start_seconds) ``` -`prepare_and_wait(video_id, *, max_on_demand_cents=0, timeout_seconds=300)` reads the Premium transcript. When the account does not own it, the call posts the purchase once, polls the job at the pace the API sets and returns the transcript. The default spends included credits only. Pass `max_on_demand_cents` only for an amount the user approved. +- `entities.resolve` answers `best` (certain), `suggested` (likeliest, with a reason; tell the user you assumed it) or `ask` (several fit; show `ask.options`). +- Entity ids look like `ent_14`. Channel ids are YouTube ids, `UC` plus 22 characters. For a show, pass `type="channel"` and use `best.youtube_channel_id`. +- A name where an id belongs raises `BadRequestError` with code `id_required`, naming the parameter. +- Dates are `after` and `before`, half-open `[after, before)`, as an ISO date or a datetime with offset. The body echoes them in `window`. + +## Premium transcripts -## Clients +`client.transcripts.get(video_id, quality="premium")` is one read. It answers `state == "ready"` (200) with the transcript, or `state == "pending"` (202) with `job` and a `Retry-After` header. Read again after `Retry-After` or `job.next_poll_seconds`. Repeated reads join the same purchase and never buy twice. The read spends included credits first, then the account's on-demand budget. `client.transcripts.quote(video_id)` is free and shows the price. Use `client.transcripts.with_raw_response.get(...)` for the status code and headers. -- `Arcmira` is synchronous. `AsyncArcmira` has the same methods to await, for example `await client.transcripts.prepare_and_wait(video_id)`. -- `client.transcripts.with_raw_response` returns the status code and headers beside the typed data. -- Every Premium purchase answers one `Job`: `client.transcripts.request(video_id=...)` submits it and `client.transcripts.status(job_id)` polls it. +## Errors -## Pagination +A refusal raises a typed error from `arcmira.errors` (`BadRequestError`, `PaymentRequiredError`, `ForbiddenError`, `NotFoundError`, `ConflictError`, `TooManyRequestsError` and others), each derived from `arcmira.core.api_error.ApiError`. `body.error` holds `type`, `code`, `message`, `param`, `gate`, `unlock` and `details`. A priced refusal (402 `quota_exceeded` or `spend_limit_exceeded`, 403 `paid_plan_required`) carries the price in `error.details.quote` and charges nothing. -List methods such as `client.transcripts.list_requests(limit=10)` and `client.channels.videos.list(channel_id=..., limit=10)` return iterators that follow `next_cursor` for you. Cursors are opaque. Keep filters the same between pages. Async clients return async iterators after awaiting the first page. +## Methods -## Errors +- `entities`: `resolve`, `get`, `momentum`. +- `mentions`: `list` (requires `entity_id`), `count`. +- `recommendations`: `list` (requires `entity_id`; `class_` is `sponsored`, `organic` or `mention`). +- `transcripts`: `search` (spoken passages, `GET /v1/search`), `get`, `quote`, `list_requests`. +- `channels`: `coverage`, `videos.list`, `sponsors.list`. +- `monitors`: `list`, `create`, `update`, `delete`, `rotate_webhook_secret`, `trackers.list`, `trackers.add`, `entities.add`, `alerts.list`. +- `trackers`: `list`, `create`, `update`, `delete`, `alerts.list`. +- `integrations`: `slack.list`. +- `feedback`: `submit`, `get`. +- `me`: `get`, `update_settings`. `health`: `check`. -An API refusal raises a typed error derived from `arcmira.core.api_error.ApiError`. Its `body` holds the public error, quote and recovery URLs. `prepare_and_wait` also raises `PreparationTimeoutError` and `PreparationFailedError`, both carrying `.job`, and `PremiumUnavailableError` when the plan has no Premium. Captions are never returned as Premium. +Follow an entity you have an id for with `monitors.entities.add(monitor_id, entity_ids=[...])`. Watch an exact name before it is indexed with `trackers.create(entity_name=..., entity_type=...)`. + +## Clients and pagination + +- `Arcmira` is synchronous. `AsyncArcmira` has the same methods to await. +- `mentions.list`, `recommendations.list`, `channels.videos.list` and `transcripts.list_requests` return pagers that follow `next_cursor`. Cursors are opaque. Keep filters the same between pages. Async pagers are async iterators after you await the first page. ## Canonical @@ -48,7 +70,6 @@ An API refusal raises a typed error derived from `arcmira.core.api_error.ApiErro - [Homepage](https://arcmira.com) - [Pricing](https://arcmira.com/pricing) - [Contact](mailto:hi@arcmira.com): hi@arcmira.com - -The MCP server ships from https://github.com/arcmira/mcp. +- [Changelog](CHANGELOG.md): What changed from 0.3, method by method. Apache-2.0. See LICENSE. diff --git a/reference.md b/reference.md index bd26f50..ed1c9b3 100644 --- a/reference.md +++ b/reference.md @@ -188,7 +188,7 @@ client.me.update_settings( ## Entities -
client.entities.search(...) -> EntitySearchResponse +
client.entities.resolve(...) -> EntityResolveResponse
@@ -200,7 +200,7 @@ client.me.update_settings(
-Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists. +Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name ("the startup bank", "on My First Million"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows.
@@ -223,7 +223,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.entities.search( +client.entities.resolve( q="q", ) @@ -241,7 +241,7 @@ client.entities.search(
-**q:** `str` +**q:** `str` — A name, @handle, YouTube URL or channel id (UC...). One thing per call.
@@ -249,7 +249,7 @@ client.entities.search(
-**type:** `typing.Optional[SearchEntitiesRequestType]` +**type:** `typing.Optional[ResolveEntitiesRequestType]` — Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id.
@@ -257,7 +257,7 @@ client.entities.search(
-**has_recommendations_data:** `typing.Optional[bool]` +**limit:** `typing.Optional[int]` — Candidates to return, 1 to 15. Default 8.
@@ -265,7 +265,7 @@ client.entities.search(
-**limit:** `typing.Optional[int]` +**context:** `typing.Optional[str]` — What the user said about the name, in their words ("the startup bank", "Canada's prime minister", "on My First Million"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context.
@@ -285,7 +285,7 @@ client.entities.search(
-
client.entities.resolve(...) -> EntityResolveResponse +
client.entities.get(...) -> EntityDetailResponse
@@ -297,7 +297,7 @@ client.entities.search(
-Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name ("the startup bank", "on My First Million"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows. +Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists.
@@ -320,8 +320,8 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.entities.resolve( - q="q", +client.entities.get( + id="id", ) ``` @@ -338,31 +338,7 @@ client.entities.resolve(
-**q:** `str` — A name, @handle, YouTube URL or channel id (UC...). One thing per call. - -
-
- -
-
- -**type:** `typing.Optional[ResolveEntitiesRequestType]` — Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Candidates to return, 1 to 15. Default 8. - -
-
- -
-
- -**context:** `typing.Optional[str]` — What the user said about the name, in their words ("the startup bank", "Canada's prime minister", "on My First Million"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context. +**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.
@@ -382,7 +358,7 @@ client.entities.resolve(
-
client.entities.lookup(...) -> EntityLookupResponse +
client.entities.momentum(...) -> EntityMomentumResponse
@@ -394,7 +370,7 @@ client.entities.resolve(
-Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type. +Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.
@@ -417,7 +393,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.entities.lookup() +client.entities.momentum( + id="id", +) ``` @@ -433,23 +411,7 @@ client.entities.lookup()
-**id:** `typing.Optional[str]` - -
-
- -
-
- -**name:** `typing.Optional[str]` - -
-
- -
-
- -**type:** `typing.Optional[LookupEntitiesRequestType]` +**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect.
@@ -469,7 +431,8 @@ client.entities.lookup()
-
client.entities.cards(...) -> EntityCardsResponse +## Mentions +
client.mentions.list(...) -> MentionListResponse
@@ -481,7 +444,7 @@ client.entities.lookup()
-Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable. +Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan.
@@ -504,8 +467,8 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.entities.cards( - ids="ids", +client.mentions.list( + entity_id="entity_id", ) ``` @@ -522,7 +485,7 @@ client.entities.cards(
-**ids:** `str` — Comma-separated raw integer entity ids, 1 to 50 of them (e.g. "12,844,1032"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response. +**entity_id:** `str` — The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.
@@ -530,72 +493,31 @@ client.entities.cards(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
- -
- +**limit:** `typing.Optional[int]` -
- -
client.entities.get(...) -> EntityDetailResponse -
-
- -#### 📝 Description
-
-
+**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. -Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists. -
-
-#### 🔌 Usage - -
-
-
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.entities.get( - id="id", -) +**channel_id:** `typing.Optional[str]` — Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. -``` -
-
-#### ⚙️ Parameters - -
-
-
-**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. +**q:** `typing.Optional[str]`
@@ -603,72 +525,39 @@ client.entities.get(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- +**sentiment:** `typing.Optional[ListMentionsRequestSentiment]`
-
-
client.entities.momentum(...) -> EntityMomentumResponse
-#### 📝 Description +**is_appearance:** `typing.Optional[bool]` -
-
+
+
-Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. -
-
+**after:** `typing.Optional[str]` — Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. +
-#### 🔌 Usage - -
-
-
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.entities.momentum( - id="id", -) +**before:** `typing.Optional[str]` — Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. -``` -
-
-#### ⚙️ Parameters - -
-
-
-**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. +**details:** `typing.Optional[ListMentionsRequestDetails]`
@@ -688,8 +577,7 @@ client.entities.momentum(
-## Mentions -
client.mentions.list(...) -> MentionListResponse +
client.mentions.count(...) -> MentionCountsResponse
@@ -701,7 +589,7 @@ client.entities.momentum(
-Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. +A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.
@@ -724,7 +612,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.mentions.list() +client.mentions.count() ``` @@ -740,47 +628,7 @@ client.mentions.list()
-**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - -
-
- -
-
- -**entity_id:** `typing.Optional[str]` - -
-
- -
-
- -**entity_name:** `typing.Optional[str]` - -
-
- -
-
- -**entity_type:** `typing.Optional[ListMentionsRequestEntityType]` - -
-
- -
-
- -**channel_id:** `typing.Optional[str]` +**channel_ids:** `typing.Optional[str]` — Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them.
@@ -788,7 +636,7 @@ client.mentions.list()
-**channel_name:** `typing.Optional[str]` +**entity_ids:** `typing.Optional[str]` — Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities.
@@ -796,7 +644,7 @@ client.mentions.list()
-**q:** `typing.Optional[str]` +**video_ids:** `typing.Optional[str]` — Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos.
@@ -804,7 +652,7 @@ client.mentions.list()
-**sentiment:** `typing.Optional[ListMentionsRequestSentiment]` +**entity_types:** `typing.Optional[str]` — Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate.
@@ -812,7 +660,7 @@ client.mentions.list()
-**is_appearance:** `typing.Optional[bool]` +**mode:** `typing.Optional[CountMentionsRequestMode]` — mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions.
@@ -820,7 +668,7 @@ client.mentions.list()
-**date_from:** `typing.Optional[str]` +**after:** `typing.Optional[str]` — Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -828,7 +676,7 @@ client.mentions.list()
-**date_to:** `typing.Optional[str]` +**before:** `typing.Optional[str]` — Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -836,7 +684,7 @@ client.mentions.list()
-**details:** `typing.Optional[ListMentionsRequestDetails]` +**limit:** `typing.Optional[int]` — Rows in the ranked table, 1 to 40. Default 20.
@@ -856,7 +704,8 @@ client.mentions.list()
-
client.mentions.count(...) -> MentionCountsResponse +## Recommendations +
client.recommendations.list(...) -> RecommendationListResponse
@@ -868,7 +717,7 @@ client.mentions.list()
-A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. +Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds).
@@ -891,7 +740,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.mentions.count() +client.recommendations.list( + entity_id="entity_id", +) ``` @@ -907,7 +758,7 @@ client.mentions.count()
-**channel_ids:** `typing.Optional[str]` — Comma-separated YouTube channel ids (UC...), at most 8. Two or more also return shared, the entities on more than one of them. +**entity_id:** `str` — The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.
@@ -915,7 +766,7 @@ client.mentions.count()
-**entity_ids:** `typing.Optional[str]` — Comma-separated entity ids (ent_{n}), at most 20. Counts only these entities. +**limit:** `typing.Optional[int]`
@@ -923,7 +774,7 @@ client.mentions.count()
-**video_ids:** `typing.Optional[str]` — Comma-separated 11-character YouTube video ids, at most 20. Counts only these episodes; take the ids from GET /v1/channels/{channel_id}/videos. +**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor.
@@ -931,7 +782,7 @@ client.mentions.count()
-**entity_types:** `typing.Optional[str]` — Comma-separated entity types to count: person, organization, product, topic, channel. Subjects are topic; guests are person; brands are organization,product. Omit and organizations dominate. +**channel_id:** `typing.Optional[str]` — Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve.
@@ -939,7 +790,7 @@ client.mentions.count()
-**mode:** `typing.Optional[CountMentionsRequestMode]` — mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions. +**class:** `typing.Optional[ListRecommendationsRequestClass]` — The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).
@@ -947,7 +798,7 @@ client.mentions.count()
-**published_after:** `typing.Optional[str]` — ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. +**min_confidence:** `typing.Optional[float]`
@@ -955,7 +806,7 @@ client.mentions.count()
-**published_before:** `typing.Optional[str]` — ISO date. Only media published before this day. +**after:** `typing.Optional[str]` — Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -963,7 +814,15 @@ client.mentions.count()
-**limit:** `typing.Optional[int]` — Rows in the ranked table, 1 to 40. Default 20. +**before:** `typing.Optional[str]` — Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. + +
+
+ +
+
+ +**include_disputed:** `typing.Optional[bool]`
@@ -983,8 +842,8 @@ client.mentions.count()
-## Recommendations -
client.recommendations.list(...) -> RecommendationListResponse +## Feedback +
client.feedback.submit(...) -> FeedbackResponse
@@ -996,7 +855,7 @@ client.mentions.count()
-Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. +Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query.
@@ -1019,7 +878,10 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.recommendations.list() +client.feedback.submit( + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + type="recommendations", +) ``` @@ -1035,7 +897,7 @@ client.recommendations.list()
-**limit:** `typing.Optional[int]` +**type:** `SubmitFeedbackRequestType` — The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional).
@@ -1043,7 +905,7 @@ client.recommendations.list()
-**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. +**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced.
@@ -1051,7 +913,7 @@ client.recommendations.list()
-**entity_id:** `typing.Optional[str]` +**query:** `typing.Optional[typing.Dict[str, typing.Any]]` — The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id.
@@ -1059,7 +921,7 @@ client.recommendations.list()
-**entity_name:** `typing.Optional[str]` +**endpoint:** `typing.Optional[str]`
@@ -1067,7 +929,7 @@ client.recommendations.list()
-**entity_type:** `typing.Optional[ListRecommendationsRequestEntityType]` +**method:** `typing.Optional[SubmitFeedbackRequestMethod]`
@@ -1075,7 +937,7 @@ client.recommendations.list()
-**channel_id:** `typing.Optional[str]` +**request_id:** `typing.Optional[str]`
@@ -1083,7 +945,7 @@ client.recommendations.list()
-**channel_name:** `typing.Optional[str]` +**result_url:** `typing.Optional[str]`
@@ -1091,7 +953,7 @@ client.recommendations.list()
-**mention_class:** `typing.Optional[ListRecommendationsRequestMentionClass]` +**source_url:** `typing.Optional[str]`
@@ -1099,7 +961,7 @@ client.recommendations.list()
-**min_confidence:** `typing.Optional[float]` +**notes:** `typing.Optional[str]` — Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing.
@@ -1107,7 +969,7 @@ client.recommendations.list()
-**date_from:** `typing.Optional[str]` +**corrections:** `typing.Optional[typing.List[SubmitFeedbackRequestCorrectionsItem]]` — Per-row corrections. Not allowed when type is experience.
@@ -1115,7 +977,7 @@ client.recommendations.list()
-**date_to:** `typing.Optional[str]` +**category:** `typing.Optional[SubmitFeedbackRequestCategory]` — What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience.
@@ -1123,7 +985,7 @@ client.recommendations.list()
-**include_disputed:** `typing.Optional[bool]` +**mcp_call_id:** `typing.Optional[str]` — The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics.
@@ -1143,8 +1005,7 @@ client.recommendations.list()
-## Feedback -
client.feedback.submit(...) -> FeedbackResponse +
client.feedback.get(...) -> FeedbackReadbackResponse
@@ -1156,156 +1017,7 @@ client.recommendations.list()
-Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.feedback.submit( - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - type="recommendations", - query={ - "key": "value" - }, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**type:** `SubmitFeedbackRequestType` — The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search). - -
-
- -
-
- -**query:** `typing.Dict[str, typing.Any]` — The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - -
-
- -
-
- -**endpoint:** `typing.Optional[str]` - -
-
- -
-
- -**method:** `typing.Optional[SubmitFeedbackRequestMethod]` - -
-
- -
-
- -**request_id:** `typing.Optional[str]` - -
-
- -
-
- -**result_url:** `typing.Optional[str]` - -
-
- -
-
- -**source_url:** `typing.Optional[str]` - -
-
- -
-
- -**notes:** `typing.Optional[str]` - -
-
- -
-
- -**corrections:** `typing.Optional[typing.List[SubmitFeedbackRequestCorrectionsItem]]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - - - -
- -
client.feedback.get(...) -> FeedbackReadbackResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Returns the submission (id, type, query, notes, created_at) plus its per-correction rows, each with a review status in the public vocabulary: pending_review, needs_information, accepted, accepted_with_changes, rejected, withdrawn, applied, reverted (accepted means a reviewer agreed; applied means the change is live in the index). Only the submitting user's keys can read a submission; unknown ids and other users' submissions both return 404 (never 403). +Returns the submission (id, type, query, notes, created_at) plus its per-correction rows, each with a review status in the public vocabulary: pending_review, needs_information, accepted, accepted_with_changes, rejected, withdrawn, applied, reverted (accepted means a reviewer agreed; applied means the change is live in the index). Only the submitting user's keys can read a submission; unknown ids and other users' submissions both return 404 (never 403).
@@ -1346,7 +1058,7 @@ client.feedback.get(
-**feedback_id:** `str` — The feedback submission id POST /v1/feedback returned. +**feedback_id:** `str` — The feedback submission id POST /v1/feedback returned, fbk_ and digits.
@@ -1379,7 +1091,7 @@ client.feedback.get(
-Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. +Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.
@@ -1468,7 +1180,7 @@ client.transcripts.search(
-**kind:** `typing.Optional[str]` — Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk. +**kind:** `typing.Optional[str]` — Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk.
@@ -1476,7 +1188,7 @@ client.transcripts.search(
-**published_after:** `typing.Optional[str]` — ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. +**after:** `typing.Optional[str]` — Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -1484,7 +1196,7 @@ client.transcripts.search(
-**published_before:** `typing.Optional[str]` — ISO date. Only media published before this day. +**before:** `typing.Optional[str]` — Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -1532,7 +1244,7 @@ client.transcripts.search(
-Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. +Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections.
@@ -1581,7 +1293,7 @@ client.transcripts.get(
-**quality:** `typing.Optional[GetTranscriptsRequestQuality]` — captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings. +**quality:** `typing.Optional[GetTranscriptsRequestQuality]` — captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.
@@ -1653,7 +1365,7 @@ client.transcripts.get(
-Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. +Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id.
@@ -1714,7 +1426,7 @@ client.transcripts.quote(
-
client.transcripts.captions(...) -> VideoCaptionsResponse +
client.transcripts.list_requests(...) -> TranscriptRequestListResponse
@@ -1726,7 +1438,7 @@ client.transcripts.quote(
-Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background. +Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`.
@@ -1749,9 +1461,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.captions( - video_id="video_id", -) +client.transcripts.list_requests() ``` @@ -1767,7 +1477,23 @@ client.transcripts.captions(
-**video_id:** `str` — YouTube video id, 11 characters. +**video_id:** `typing.Optional[str]` — Filter to your requests for one video. + +
+
+ +
+
+ +**limit:** `typing.Optional[int]` — Requests per page, from 1 to 100. Default 20. + +
+
+ +
+
+ +**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Keep the same filter, limit and credential.
@@ -1787,7 +1513,8 @@ client.transcripts.captions(
-
client.transcripts.list_requests(...) -> TranscriptRequestListResponse +## Channels +
client.channels.coverage(...) -> ChannelCoverageResponse
@@ -1799,7 +1526,7 @@ client.transcripts.captions(
-Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`. +How many videos of a YouTube channel are searchable, the newest publish date among them, and the split by transcript source class. Call it when a search or mention lookup came back empty, before telling anyone we do not cover a show, and cite indexed_through as the as-of date for mentions and search_indexed_through for transcript search. It cannot request indexing; channel backfill is not available yet. Free (0 rows). Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.
@@ -1822,7 +1549,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.list_requests() +client.channels.coverage( + channel_id="channel_id", +) ``` @@ -1838,23 +1567,7 @@ client.transcripts.list_requests()
-**video_id:** `typing.Optional[str]` — Filter to your requests for one video. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Requests per page, from 1 to 100. Default 20. - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Keep the same filter, limit and credential. +**channel_id:** `str` — YouTube channel id, the UC... form.
@@ -1874,7 +1587,8 @@ client.transcripts.list_requests()
-
client.transcripts.request(...) -> TranscriptRequestSubmitResponse +## Monitors +
client.monitors.list() -> MonitorListResponse
@@ -1886,7 +1600,7 @@ client.transcripts.list_requests()
-Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it. +All monitors for the account with tracker counts, alert counts for the current calendar month, and Slack display metadata. Single page, no pagination.
@@ -1909,9 +1623,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.request( - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", -) +client.monitors.list() ``` @@ -1927,46 +1639,6 @@ client.transcripts.request(
-**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - -
-
- -
-
- -**video_id:** `typing.Optional[str]` — YouTube video id (11 characters). Either video_id or url is required. - -
-
- -
-
- -**url:** `typing.Optional[str]` — A YouTube watch/short/live URL. Either video_id or url is required. - -
-
- -
-
- -**max_rows:** `typing.Optional[int]` — Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0. - -
-
- -
-
- -**max_on_demand_cents:** `typing.Optional[int]` — Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows. - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -1979,7 +1651,7 @@ client.transcripts.request(
-
client.transcripts.status(...) -> TranscriptJob +
client.monitors.create(...) -> MonitorMutationResponse
@@ -1991,7 +1663,7 @@ client.transcripts.request(
-Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up. +Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint.
@@ -2014,8 +1686,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.status( - id="id", +client.monitors.create( + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + name="name", ) ``` @@ -2032,7 +1705,7 @@ client.transcripts.status(
-**id:** `str` — Transcription request id, the UUID POST /v1/transcriptions returned. +**name:** `str` — Display name (1-100 characters). Required on create.
@@ -2040,73 +1713,87 @@ client.transcripts.status(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
- -
+
+
+ +**notify_emails:** `typing.Optional[typing.List[str]]` — Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default [].
-
-## Channels -
client.channels.coverage(...) -> ChannelCoverageResponse
-#### 📝 Description +**notify_frequency:** `typing.Optional[CreateMonitorsRequestNotifyFrequency]` — Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent. -
-
+
+
-How many videos of a YouTube channel are searchable, the newest publish date among them, and the split by transcript source class. Call it when a search or mention lookup came back empty, before telling anyone we do not cover a show, and cite indexed_through as the as-of date for mentions and search_indexed_through for transcript search. It cannot request indexing; channel backfill is not available yet. Free (0 rows). Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. +**digest_day:** `typing.Optional[str]` — Digest day of week. Default "monday". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies. +
+ +
+
+ +**digest_time:** `typing.Optional[str]` — Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. +
-#### 🔌 Usage -
+**notify_webhook:** `typing.Optional[bool]` — Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + +
+
+
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment +**webhook_url:** `typing.Optional[str]` — Destination URL for webhook alert deliveries. -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) +
+
-client.channels.coverage( - channel_id="channel_id", -) +
+
+ +**notify_slack:** `typing.Optional[bool]` — Enable Slack delivery. Requires a Slack integration connected in the dashboard. -```
+ +
+
+ +**slack_integration_id:** `typing.Optional[str]` — Slack integration id from the dashboard OAuth flow. +
-#### ⚙️ Parameters -
+**slack_channel_id:** `typing.Optional[str]` — Slack channel id to deliver to. + +
+
+
-**channel_id:** `str` — YouTube channel id, the UC... form. +**team_id:** `typing.Optional[str]` — Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only.
@@ -2126,7 +1813,7 @@ client.channels.coverage(
-
client.channels.get(...) -> ChannelPageResponse +
client.monitors.delete(...) -> MonitorDeleteResponse
@@ -2138,7 +1825,7 @@ client.channels.coverage(
-Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan. +Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again.
@@ -2161,8 +1848,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.channels.get( - slug="slug", +client.monitors.delete( + id="id", + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", ) ``` @@ -2179,7 +1867,15 @@ client.channels.get(
-**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. +**id:** `str` — Monitor id. + +
+
+ +
+
+ +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
@@ -2199,11 +1895,24 @@ client.channels.get(
-## People -
client.people.get(...) -> PersonPageResponse +
client.monitors.update(...) -> MonitorMutationResponse +
+
+ +#### 📝 Description + +
+
+
+A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. +
+
+
+
+ #### 🔌 Usage
@@ -2221,8 +1930,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.people.get( - slug="slug", +client.monitors.update( + id="id", + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", ) ``` @@ -2239,7 +1949,7 @@ client.people.get(
-**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. +**id:** `str` — Monitor id.
@@ -2247,59 +1957,47 @@ client.people.get(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
-
-
+
+
+ +**name:** `typing.Optional[str]` — Display name (1-100 characters). Required on create.
-
-## Topics -
client.topics.get(...) -> TopicPageResponse
-#### 🔌 Usage +**notify_emails:** `typing.Optional[typing.List[str]]` — Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []. -
-
+
+
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.get( - slug="slug", -) +**notify_frequency:** `typing.Optional[UpdateMonitorsRequestNotifyFrequency]` — Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent. -```
-
-
- -#### ⚙️ Parameters
+**digest_day:** `typing.Optional[str]` — Digest day of week. Default "monday". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies. + +
+
+
-**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. +**digest_time:** `typing.Optional[str]` — Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests.
@@ -2307,59 +2005,47 @@ client.topics.get(
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +**notify_webhook:** `typing.Optional[bool]` — Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter.
- - +
+
+ +**webhook_url:** `typing.Optional[str]` — Destination URL for webhook alert deliveries.
-
-## Organizations -
client.organizations.get(...) -> OrganizationPageResponse
-#### 🔌 Usage +**notify_slack:** `typing.Optional[bool]` — Enable Slack delivery. Requires a Slack integration connected in the dashboard. -
-
+
+
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.get( - slug="slug", -) +**slack_integration_id:** `typing.Optional[str]` — Slack integration id from the dashboard OAuth flow. -```
-
-
- -#### ⚙️ Parameters
+**slack_channel_id:** `typing.Optional[str]` — Slack channel id to deliver to. + +
+
+
-**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. +**paused:** `typing.Optional[bool]` — Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued.
@@ -2379,11 +2065,24 @@ client.organizations.get(
-## Products -
client.products.get(...) -> ProductPageResponse +
client.monitors.rotate_webhook_secret(...) -> WebhookSecretRotateResponse +
+
+ +#### 📝 Description + +
+
+
+Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope. +
+
+
+
+ #### 🔌 Usage
@@ -2401,8 +2100,9 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.products.get( - slug="slug", +client.monitors.rotate_webhook_secret( + id="id", + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", ) ``` @@ -2419,7 +2119,15 @@ client.products.get(
-**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. +**id:** `str` — Monitor id. + +
+
+ +
+
+ +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
@@ -2439,8 +2147,8 @@ client.products.get(
-## Monitors -
client.monitors.list() -> MonitorListResponse +## Trackers +
client.trackers.list() -> TrackerListResponse
@@ -2452,7 +2160,7 @@ client.products.get(
-All monitors for the account with tracker counts, alert counts for the current calendar month, and Slack display metadata. Single page, no pagination. +All trackers for the account, newest first, with per-channel delivery counts for the current billing period. Single page, no pagination.
@@ -2475,7 +2183,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.monitors.list() +client.trackers.list() ``` @@ -2503,7 +2211,7 @@ client.monitors.list()
-
client.monitors.create(...) -> MonitorMutationResponse +
client.trackers.create(...) -> TrackerMutationResponse
@@ -2515,7 +2223,7 @@ client.monitors.list()
-Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint. +Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id.
@@ -2538,9 +2246,10 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.monitors.create( +client.trackers.create( idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - name="name", + entity_name="entity_name", + entity_type="person", ) ``` @@ -2557,7 +2266,15 @@ client.monitors.create(
-**name:** `str` — Display name (1-100 characters). Required on create. +**entity_name:** `str` — The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. + +
+
+ +
+
+ +**entity_type:** `CreateTrackersRequestEntityType` — Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization.
@@ -2573,7 +2290,7 @@ client.monitors.create(
-**notify_emails:** `typing.Optional[typing.List[str]]` — Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []. +**display_name:** `typing.Optional[str]` — Optional label shown in alerts and the dashboard.
@@ -2581,7 +2298,7 @@ client.monitors.create(
-**notify_frequency:** `typing.Optional[CreateMonitorsRequestNotifyFrequency]` — Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent. +**notify_email:** `typing.Optional[bool]` — Per-tracker email delivery. Default true.
@@ -2589,7 +2306,7 @@ client.monitors.create(
-**digest_day:** `typing.Optional[str]` — Digest day of week. Default "monday". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies. +**notify_webhook:** `typing.Optional[bool]` — Per-tracker webhook delivery override. Paid plans only.
@@ -2597,7 +2314,7 @@ client.monitors.create(
-**digest_time:** `typing.Optional[str]` — Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. +**notify_slack:** `typing.Optional[bool]` — Per-tracker Slack delivery override. Paid plans only.
@@ -2605,7 +2322,7 @@ client.monitors.create(
-**notify_webhook:** `typing.Optional[bool]` — Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. +**webhook_url:** `typing.Optional[str]` — Per-tracker webhook destination override (http/https).
@@ -2613,7 +2330,7 @@ client.monitors.create(
-**webhook_url:** `typing.Optional[str]` — Destination URL for webhook alert deliveries. +**slack_channel_id:** `typing.Optional[str]` — Per-tracker Slack channel override.
@@ -2621,7 +2338,7 @@ client.monitors.create(
-**notify_slack:** `typing.Optional[bool]` — Enable Slack delivery. Requires a Slack integration connected in the dashboard. +**slack_integration_id:** `typing.Optional[str]` — Per-tracker Slack integration override.
@@ -2629,7 +2346,7 @@ client.monitors.create(
-**slack_integration_id:** `typing.Optional[str]` — Slack integration id from the dashboard OAuth flow. +**person_match_mode:** `typing.Optional[CreateTrackersRequestPersonMatchMode]` — Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill.
@@ -2637,7 +2354,7 @@ client.monitors.create(
-**slack_channel_id:** `typing.Optional[str]` — Slack channel id to deliver to. +**filters:** `typing.Optional[typing.Dict[str, typing.Any]]` — Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching.
@@ -2657,7 +2374,7 @@ client.monitors.create(
-
client.monitors.delete(...) -> MonitorDeleteResponse +
client.trackers.delete(...) -> MessageResponse
@@ -2669,7 +2386,7 @@ client.monitors.create(
-Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. +Deletes the tracker. Cannot be undone.
@@ -2692,7 +2409,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.monitors.delete( +client.trackers.delete( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", ) @@ -2711,7 +2428,7 @@ client.monitors.delete(
-**id:** `str` — Monitor id. +**id:** `str` — Tracker id, trk_ form.
@@ -2739,7 +2456,7 @@ client.monitors.delete(
-
client.monitors.update(...) -> MonitorMutationResponse +
client.trackers.update(...) -> TrackerMutationResponse
@@ -2751,7 +2468,7 @@ client.monitors.delete(
-A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. +Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity.
@@ -2774,7 +2491,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.monitors.update( +client.trackers.update( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", ) @@ -2793,7 +2510,7 @@ client.monitors.update(
-**id:** `str` — Monitor id. +**id:** `str` — Tracker id, trk_ form.
@@ -2809,7 +2526,7 @@ client.monitors.update(
-**name:** `typing.Optional[str]` — Display name (1-100 characters). Required on create. +**display_name:** `typing.Optional[str]` — Optional label shown in alerts and the dashboard.
@@ -2817,7 +2534,7 @@ client.monitors.update(
-**notify_emails:** `typing.Optional[typing.List[str]]` — Desired email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor; paid plans allow up to 20 total. Default []. +**notify_email:** `typing.Optional[bool]` — Per-tracker email delivery. Default true.
@@ -2825,7 +2542,7 @@ client.monitors.update(
-**notify_frequency:** `typing.Optional[UpdateMonitorsRequestNotifyFrequency]` — Delivery cadence. Default realtime. Values: realtime (as analysis completes), hourly (hourly digest), daily (daily digest). Free-tier email delivery is coerced to daily regardless of the value sent. +**notify_webhook:** `typing.Optional[bool]` — Per-tracker webhook delivery override. Paid plans only.
@@ -2833,7 +2550,7 @@ client.monitors.update(
-**digest_day:** `typing.Optional[str]` — Digest day of week. Default "monday". Consulted only by weekly digests, which are dashboard-configured today; inert for API-set frequencies. +**notify_slack:** `typing.Optional[bool]` — Per-tracker Slack delivery override. Paid plans only.
@@ -2841,7 +2558,7 @@ client.monitors.update(
-**digest_time:** `typing.Optional[str]` — Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. +**webhook_url:** `typing.Optional[str]` — Per-tracker webhook destination override (http/https).
@@ -2849,7 +2566,7 @@ client.monitors.update(
-**notify_webhook:** `typing.Optional[bool]` — Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. +**slack_channel_id:** `typing.Optional[str]` — Per-tracker Slack channel override.
@@ -2857,7 +2574,7 @@ client.monitors.update(
-**webhook_url:** `typing.Optional[str]` — Destination URL for webhook alert deliveries. +**slack_integration_id:** `typing.Optional[str]` — Per-tracker Slack integration override.
@@ -2865,7 +2582,7 @@ client.monitors.update(
-**notify_slack:** `typing.Optional[bool]` — Enable Slack delivery. Requires a Slack integration connected in the dashboard. +**person_match_mode:** `typing.Optional[UpdateTrackersRequestPersonMatchMode]` — Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill.
@@ -2873,7 +2590,7 @@ client.monitors.update(
-**slack_integration_id:** `typing.Optional[str]` — Slack integration id from the dashboard OAuth flow. +**filters:** `typing.Optional[typing.Dict[str, typing.Any]]` — Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching.
@@ -2881,7 +2598,7 @@ client.monitors.update(
-**slack_channel_id:** `typing.Optional[str]` — Slack channel id to deliver to. +**paused:** `typing.Optional[bool]` — Pause or resume the tracker. Paused trackers stop producing alerts; there is no backfill for the paused window.
@@ -2889,5427 +2606,24 @@ client.monitors.update(
-**is_paused:** `typing.Optional[bool]` — Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued. +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+ +
-
-
- -**is_collapsed:** `typing.Optional[bool]` — Dashboard display state.
+
+## Channels Sponsors +
client.channels.sponsors.list(...) -> ChannelSponsorsResponse
-**sort_order:** `typing.Optional[int]` — Dashboard sort position. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
- - - - - - -
- -
client.monitors.rotate_webhook_secret(...) -> WebhookSecretRotateResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.monitors.rotate_webhook_secret( - id="id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Monitor id. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Trackers -
client.trackers.list() -> TrackerListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -All trackers for the account, newest first, with per-channel delivery counts for the current billing period. Single page, no pagination. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.trackers.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trackers.create(...) -> TrackerMutationResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.trackers.create( - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_name="entityName", - entity_type="person", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**entity_name:** `str` — The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId. - -
-
- -
-
- -**entity_type:** `CreateTrackersRequestEntityType` — Entity type of the tracked entity. Required on create. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - -
-
- -
-
- -**display_name:** `typing.Optional[str]` — Optional label shown in alerts and the dashboard. - -
-
- -
-
- -**notify_email:** `typing.Optional[bool]` — Per-tracker email delivery. Default true. - -
-
- -
-
- -**notify_webhook:** `typing.Optional[bool]` — Per-tracker webhook delivery override. Paid plans only. - -
-
- -
-
- -**notify_slack:** `typing.Optional[bool]` — Per-tracker Slack delivery override. Paid plans only. - -
-
- -
-
- -**webhook_url:** `typing.Optional[str]` — Per-tracker webhook destination override (http/https). - -
-
- -
-
- -**slack_channel_id:** `typing.Optional[str]` — Per-tracker Slack channel override. - -
-
- -
-
- -**slack_integration_id:** `typing.Optional[str]` — Per-tracker Slack integration override. - -
-
- -
-
- -**person_match_mode:** `typing.Optional[CreateTrackersRequestPersonMatchMode]` — Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill. - -
-
- -
-
- -**filters:** `typing.Optional[typing.Dict[str, typing.Any]]` — Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trackers.delete(...) -> MessageResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Deletes the tracker. Cannot be undone. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.trackers.delete( - id="id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Tracker id, trk_ form. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.trackers.update(...) -> TrackerMutationResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.trackers.update( - id="id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Tracker id, trk_ form. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - -
-
- -
-
- -**display_name:** `typing.Optional[str]` — Optional label shown in alerts and the dashboard. - -
-
- -
-
- -**notify_email:** `typing.Optional[bool]` — Per-tracker email delivery. Default true. - -
-
- -
-
- -**notify_webhook:** `typing.Optional[bool]` — Per-tracker webhook delivery override. Paid plans only. - -
-
- -
-
- -**notify_slack:** `typing.Optional[bool]` — Per-tracker Slack delivery override. Paid plans only. - -
-
- -
-
- -**webhook_url:** `typing.Optional[str]` — Per-tracker webhook destination override (http/https). - -
-
- -
-
- -**slack_channel_id:** `typing.Optional[str]` — Per-tracker Slack channel override. - -
-
- -
-
- -**slack_integration_id:** `typing.Optional[str]` — Per-tracker Slack integration override. - -
-
- -
-
- -**person_match_mode:** `typing.Optional[UpdateTrackersRequestPersonMatchMode]` — Person trackers only. Mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Non-person trackers reject this field. PATCH changes future and pending delivery eligibility, without backfill. - -
-
- -
-
- -**filters:** `typing.Optional[typing.Dict[str, typing.Any]]` — Stored filter object. personMatchMode is also accepted here for person trackers. Other filter keys are retained; do not assume they change matching. - -
-
- -
-
- -**paused:** `typing.Optional[bool]` — Pause or resume the tracker. Paused trackers stop producing alerts; there is no backfill for the paused window. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Team -
client.team.members() -> TeamMembersResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.team.members() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.team.spend() -> TeamSpendResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.team.spend() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Corrections -
client.corrections.submit(...) -> CorrectionAcceptedResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.corrections.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - kind="line_edit", - payload={ - "key": "value" - }, -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**video_id:** `str` — YouTube video id, 11 characters. - -
-
- -
-
- -**kind:** `SubmitCorrectionsRequestKind` - -
-
- -
-
- -**payload:** `typing.Dict[str, typing.Any]` — Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once. - -
-
- -
-
- -**seq:** `typing.Optional[int]` — Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value. - -
-
- -
-
- -**revision:** `typing.Optional[str]` — The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read. - -
-
- -
-
- -**anchor:** `typing.Optional[SubmitCorrectionsRequestAnchor]` — Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.corrections.withdraw_speaker_edit(...) -> WithdrawnResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.corrections.withdraw_speaker_edit( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — The pending row id, as result.id of the response that accepted it. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.corrections.withdraw_entity_tag(...) -> WithdrawnResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.corrections.withdraw_entity_tag( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — The pending row id, as result.id of the response that accepted it. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.corrections.withdraw_segment_rewrite(...) -> WithdrawnResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.corrections.withdraw_segment_rewrite( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — The pending row id, as result.id of the response that accepted it. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Channels Sponsors -
client.channels.sponsors.list(...) -> ChannelSponsorsResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Rollup of recurring sponsors for a YouTube channel, ordered by ad read count. On a Pro+ plan the full list is served and min_ad_reads (default 3), status, and limit apply. Every other plan receives the free slice an anonymous visitor sees on arcmira.com, with meta.total naming the true count and access naming the gate; passing min_ad_reads, status, or limit on such a plan is refused with filter_requires_paid. Pass src=mcp-tool only from the Arcmira MCP server. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.sponsors.list( - channel_id="channel_id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**channel_id:** `str` — YouTube channel id, the UC... form. - -
-
- -
-
- -**min_ad_reads:** `typing.Optional[int]` — Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid. - -
-
- -
-
- -**status:** `typing.Optional[ListSponsorsRequestStatus]` — Filter against the curated known-advertisers dataset. Pro+ only. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Sponsors to return. Default 100. Pro+ only; other plans receive the free slice. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Channels Videos -
client.channels.videos.list(...) -> ChannelVideosResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The indexed videos of a YouTube channel, newest first, each with its video_id, title, publish date, duration, view count, and watch_url on arcmira.com. Pass next_cursor as cursor to continue. The signed token binds the route, filters, caller and visibility; invalid or old tokens return invalid_cursor. A first-page media ID fence excludes later insertions, including old-date backfills; edits and deletions to existing rows remain live. Call it for the latest or most recent episode of a show, or to list what a show published in a window, then pass a video_id to GET /v1/mentions/counts video_ids for what that episode mentions or to GET /v1/transcripts/{video_id} to read it. indexed_through is the newest date we hold for the channel. An empty list means nothing is indexed; channel backfill is not available yet. Bills one row per video returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.videos.list( - channel_id="channel_id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**channel_id:** `str` — YouTube channel id, the UC... form. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode. - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. - -
-
- -
-
- -**published_after:** `typing.Optional[str]` — ISO date. Only videos published on or after this day. - -
-
- -
-
- -**published_before:** `typing.Optional[str]` — ISO date. Only videos published before this day. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Channels Related -
client.channels.related.topics(...) -> EntityTopicListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.related.topics( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[TopicsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[TopicsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[TopicsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.channels.related.people(...) -> EntityPeopleListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.related.people( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[PeopleRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[PeopleRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[PeopleRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.channels.related.organizations(...) -> EntityOrganizationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.related.organizations( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[OrganizationsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[OrganizationsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[OrganizationsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.channels.related.products(...) -> EntityProductListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.related.products( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ProductsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ProductsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ProductsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.channels.related.channels(...) -> EntityChannelListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.related.channels( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ChannelsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ChannelsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ChannelsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Channels Guests -
client.channels.guests.list(...) -> ChannelGuestListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.channels.guests.list( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ListGuestsRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ListGuestsRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ListGuestsRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Entities Mentions -
client.entities.mentions.list(...) -> MentionListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.entities.mentions.list( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - -
-
- -
-
- -**channel_id:** `typing.Optional[str]` - -
-
- -
-
- -**channel_name:** `typing.Optional[str]` - -
-
- -
-
- -**q:** `typing.Optional[str]` - -
-
- -
-
- -**sentiment:** `typing.Optional[ListMentionsRequestSentiment]` - -
-
- -
-
- -**is_appearance:** `typing.Optional[bool]` - -
-
- -
-
- -**date_from:** `typing.Optional[str]` - -
-
- -
-
- -**date_to:** `typing.Optional[str]` - -
-
- -
-
- -**details:** `typing.Optional[ListMentionsRequestDetails]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Entities Recommendations -
client.entities.recommendations.list(...) -> RecommendationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.entities.recommendations.list( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - -
-
- -
-
- -**channel_id:** `typing.Optional[str]` - -
-
- -
-
- -**channel_name:** `typing.Optional[str]` - -
-
- -
-
- -**mention_class:** `typing.Optional[ListRecommendationsRequestMentionClass]` - -
-
- -
-
- -**min_confidence:** `typing.Optional[float]` - -
-
- -
-
- -**date_from:** `typing.Optional[str]` - -
-
- -
-
- -**date_to:** `typing.Optional[str]` - -
-
- -
-
- -**include_disputed:** `typing.Optional[bool]` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Monitors Trackers -
client.monitors.trackers.list(...) -> MonitorTrackersResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.monitors.trackers.list( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Monitor id. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.monitors.trackers.add(...) -> MonitorAddTrackersResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Attaches EXISTING trackers to the monitor by id ({ trackerIds: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.monitors.trackers.add( - id="id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - tracker_ids=[ - "trackerIds" - ], -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Monitor id. - -
-
- -
-
- -**tracker_ids:** `typing.List[str]` — Ids of existing trackers ("trk_...") to attach to this monitor. Create trackers first via POST /v1/trackers. At most 90 IDs per request; duplicates count once. All IDs must belong to the account or none are attached. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Monitors Alerts -
client.monitors.alerts.list(...) -> AlertListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.monitors.alerts.list( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `str` — Monitor id. - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Alerts to return, newest first, 1 to 100. Default 25. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Organizations Related -
client.organizations.related.topics(...) -> EntityTopicListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.related.topics( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[TopicsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[TopicsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[TopicsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.related.people(...) -> EntityPeopleListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.related.people( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[PeopleRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[PeopleRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[PeopleRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.related.organizations(...) -> EntityOrganizationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.related.organizations( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[OrganizationsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[OrganizationsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[OrganizationsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.related.products(...) -> EntityProductListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.related.products( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ProductsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ProductsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ProductsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.organizations.related.channels(...) -> EntityChannelListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.organizations.related.channels( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ChannelsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ChannelsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ChannelsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## People Appearances -
client.people.appearances.list(...) -> PersonAppearanceListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.appearances.list( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ListAppearancesRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ListAppearancesRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ListAppearancesRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## People Related -
client.people.related.topics(...) -> EntityTopicListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.related.topics( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[TopicsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[TopicsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[TopicsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.people.related.people(...) -> EntityPeopleListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.related.people( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[PeopleRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[PeopleRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[PeopleRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.people.related.organizations(...) -> EntityOrganizationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.related.organizations( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[OrganizationsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[OrganizationsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[OrganizationsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.people.related.products(...) -> EntityProductListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.related.products( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ProductsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ProductsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ProductsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.people.related.channels(...) -> EntityChannelListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.people.related.channels( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ChannelsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ChannelsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ChannelsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Products Related -
client.products.related.topics(...) -> EntityTopicListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.products.related.topics( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[TopicsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[TopicsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[TopicsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.products.related.people(...) -> EntityPeopleListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.products.related.people( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[PeopleRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[PeopleRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[PeopleRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.products.related.organizations(...) -> EntityOrganizationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.products.related.organizations( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[OrganizationsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[OrganizationsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[OrganizationsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.products.related.products(...) -> EntityProductListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.products.related.products( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ProductsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ProductsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ProductsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.products.related.channels(...) -> EntityChannelListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.products.related.channels( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ChannelsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ChannelsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ChannelsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Team UsageEvents -
client.team.usage_events.list(...) -> TeamUsageEventsResponse -
-
- -#### 📝 Description - -
-
- -
-
- -Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.team.usage_events.list() - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**limit:** `typing.Optional[int]` — Events per page, 1 to 100. - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Opaque cursor from a previous page's next_cursor. - -
-
- -
-
- -**days:** `typing.Optional[int]` — Look-back window in days, bounded at 90. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -## Topics Related -
client.topics.related.topics(...) -> EntityTopicListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.related.topics( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[TopicsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[TopicsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[TopicsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.topics.related.people(...) -> EntityPeopleListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.related.people( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[PeopleRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[PeopleRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[PeopleRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.topics.related.organizations(...) -> EntityOrganizationListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.related.organizations( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[OrganizationsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[OrganizationsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[OrganizationsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.topics.related.products(...) -> EntityProductListResponse -
-
- -#### 📝 Description - -
-
- -
-
- -The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.related.products( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). - -
-
- -
-
- -**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
- -
-
- -**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - -
-
- -
-
- -**order:** `typing.Optional[ProductsRelatedRequestOrder]` — Sort direction. Default desc. - -
-
- -
-
- -**mode:** `typing.Optional[ProductsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. - -
-
- -
-
- -**is_appearance:** `typing.Optional[ProductsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.topics.related.channels(...) -> EntityChannelListResponse -
-
- -#### 📝 Description +#### 📝 Description
@@ -8317,7 +2631,7 @@ client.topics.related.products(
-The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. +Rollup of recurring sponsors for a YouTube channel, ordered by ad read count. On a Pro+ plan the full list is served and min_ad_reads (default 3), status, and limit apply. Every other plan receives the free slice an anonymous visitor sees on arcmira.com, with meta.total naming the true count and access naming the gate; passing min_ad_reads, status, or limit on such a plan is refused with filter_requires_paid. Pass src=mcp-tool only from the Arcmira MCP server.
@@ -8331,74 +2645,34 @@ The channels that co-occur with this topic in indexed media, with q/field/sort/o
-```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.topics.related.channels( - slug="slug", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**slug:** `str` — The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - -
-
- -
-
- -**limit:** `typing.Optional[int]` - -
-
- -
-
- -**cursor:** `typing.Optional[str]` — Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - -
-
- -
-
- -**q:** `typing.Optional[str]` — Substring filter over the row's text columns (e.g. video title, channel name, description). +```python +from arcmira import Arcmira +from arcmira.environment import ArcmiraEnvironment + +client = Arcmira( + api_key="", + environment=ArcmiraEnvironment.DEFAULT, +) +client.channels.sponsors.list( + channel_id="channel_id", +) + +``` +
+
+#### ⚙️ Parameters +
-**field:** `typing.Optional[str]` — Restrict the q filter to one column. Default "any" (all searchable columns). - -
-
-
-**sort:** `typing.Optional[str]` — Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). +**channel_id:** `str` — YouTube channel id, the UC... form.
@@ -8406,7 +2680,7 @@ client.topics.related.channels(
-**order:** `typing.Optional[ChannelsRelatedRequestOrder]` — Sort direction. Default desc. +**min_ad_reads:** `typing.Optional[int]` — Sponsors with fewer ad reads are excluded. Default 3. Pro+ only; on other plans passing it is refused with filter_requires_paid.
@@ -8414,7 +2688,7 @@ client.topics.related.channels(
-**mode:** `typing.Optional[ChannelsRelatedRequestMode]` — Person relationship lens: guest appearances or inbound mentions. +**status:** `typing.Optional[ListSponsorsRequestStatus]` — Filter against the curated known-advertisers dataset. Pro+ only.
@@ -8422,7 +2696,7 @@ client.topics.related.channels(
-**is_appearance:** `typing.Optional[ChannelsRelatedRequestIsAppearance]` — For person appearances, true lists guest episodes and false lists inbound mentions. +**limit:** `typing.Optional[int]` — Sponsors to return. Default 100. Pro+ only; other plans receive the free slice.
@@ -8442,8 +2716,8 @@ client.topics.related.channels(
-## Trackers Alerts -
client.trackers.alerts.list(...) -> AlertListResponse +## Channels Videos +
client.channels.videos.list(...) -> ChannelVideosResponse
@@ -8455,7 +2729,7 @@ client.topics.related.channels(
-The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. +The indexed videos of a YouTube channel, newest first, each with its video_id, title, publish date, duration, view count, and watch_url on arcmira.com. Pass next_cursor as cursor to continue. The signed token binds the route, filters, caller and visibility; invalid or old tokens return invalid_cursor. A first-page media ID fence excludes later insertions, including old-date backfills; edits and deletions to existing rows remain live. Call it for the latest or most recent episode of a show, or to list what a show published in a window, then pass a video_id to GET /v1/mentions/counts video_ids for what that episode mentions or to GET /v1/transcripts/{video_id} to read it. indexed_through is the newest date we hold for the channel. An empty list means nothing is indexed; channel backfill is not available yet. Bills one row per video returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server.
@@ -8478,8 +2752,8 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.trackers.alerts.list( - id="id", +client.channels.videos.list( + channel_id="channel_id", ) ``` @@ -8496,7 +2770,7 @@ client.trackers.alerts.list(
-**id:** `str` — Tracker id, trk_ form. +**channel_id:** `str` — YouTube channel id, the UC... form.
@@ -8504,7 +2778,31 @@ client.trackers.alerts.list(
-**limit:** `typing.Optional[int]` — Alerts to return, newest first, 1 to 100. Default 25. +**limit:** `typing.Optional[int]` — Videos to return, 1 to 25, newest first. Default 10. Pass 1 for the latest episode. + +
+
+ +
+
+ +**cursor:** `typing.Optional[str]` — Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. + +
+
+ +
+
+ +**after:** `typing.Optional[str]` — Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. + +
+
+ +
+
+ +**before:** `typing.Optional[str]` — Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive.
@@ -8524,8 +2822,8 @@ client.trackers.alerts.list(
-## Transcripts Edits -
client.transcripts.edits.submit(...) -> TranscriptEditSubmittedResponse +## Integrations Slack +
client.integrations.slack.list() -> SlackIntegrationListResponse
@@ -8537,7 +2835,7 @@ client.trackers.alerts.list(
-Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key. +The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination.
@@ -8560,13 +2858,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.edits.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - segment_index=1, - original_text="originalText", - corrected_text="correctedText", -) +client.integrations.slack.list() ``` @@ -8582,54 +2874,6 @@ client.transcripts.edits.submit(
-**video_id:** `str` — YouTube video id, 11 characters. - -
-
- -
-
- -**segment_index:** `int` - -
-
- -
-
- -**original_text:** `str` — The current segment text you are correcting (guards against applying to a changed segment). - -
-
- -
-
- -**corrected_text:** `str` - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - -
-
- -
-
- -**revision:** `typing.Optional[str]` — The revision from the Premium transcript read. - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
@@ -8642,7 +2886,8 @@ client.transcripts.edits.submit(
-
client.transcripts.edits.withdraw(...) -> WithdrawnResponse +## Monitors Trackers +
client.monitors.trackers.list(...) -> MonitorTrackersResponse
@@ -8663,8 +2908,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.edits.withdraw( - video_id="video_id", +client.monitors.trackers.list( id="id", ) @@ -8682,15 +2926,7 @@ client.transcripts.edits.withdraw(
-**video_id:** `str` — YouTube video id, 11 characters. - -
-
- -
-
- -**id:** `str` — The pending row id, as result.id of the response that accepted it. +**id:** `str` — Monitor id.
@@ -8710,8 +2946,7 @@ client.transcripts.edits.withdraw(
-## Transcripts Speakers -
client.transcripts.speakers.identify(...) -> SpeakerIdentificationSubmittedResponse +
client.monitors.trackers.add(...) -> MonitorAddTrackersResponse
@@ -8723,7 +2958,7 @@ client.transcripts.edits.withdraw(
-Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows). +Attaches EXISTING trackers to the monitor by id ({ tracker_ids: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count.
@@ -8746,10 +2981,12 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.speakers.identify( - video_id="video_id", +client.monitors.trackers.add( + id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - speaker_id=1, + tracker_ids=[ + "tracker_ids" + ], ) ``` @@ -8766,31 +3003,7 @@ client.transcripts.speakers.identify(
-**video_id:** `str` — YouTube video id, 11 characters. - -
-
- -
-
- -**speaker_id:** `int` — A speakers[].id from the Premium transcript read that revision names. - -
-
- -
-
- -**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - -
-
- -
-
- -**entity_id:** `typing.Optional[int]` — Existing person entity id. Either entityId or name is required. +**id:** `str` — Monitor id.
@@ -8798,7 +3011,7 @@ client.transcripts.speakers.identify(
-**name:** `typing.Optional[str]` — Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required. +**tracker_ids:** `typing.List[str]` — Ids of existing trackers ("trk_...") to attach to this monitor. Create trackers first via POST /v1/trackers. At most 90 IDs per request; duplicates count once. All IDs must belong to the account or none are attached.
@@ -8806,7 +3019,7 @@ client.transcripts.speakers.identify(
-**revision:** `typing.Optional[str]` — The revision of the transcript read speakerId came from. Required. +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
@@ -8826,7 +3039,8 @@ client.transcripts.speakers.identify(
-
client.transcripts.speakers.withdraw(...) -> WithdrawnResponse +## Monitors Alerts +
client.monitors.alerts.list(...) -> AlertListResponse
@@ -8838,7 +3052,7 @@ client.transcripts.speakers.identify(
-Withdrawing also removes the community-attributed appearance the identification created. +The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.
@@ -8861,8 +3075,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.speakers.withdraw( - video_id="video_id", +client.monitors.alerts.list( id="id", ) @@ -8880,75 +3093,15 @@ client.transcripts.speakers.withdraw(
-**video_id:** `str` — YouTube video id, 11 characters. - -
-
- -
-
- -**id:** `str` — The pending row id, as result.id of the response that accepted it. - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
- -
- - - - -
- -## Transcripts Merges -
client.transcripts.merges.list(...) -> VideoMergeListResponse -
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from arcmira import Arcmira -from arcmira.environment import ArcmiraEnvironment - -client = Arcmira( - api_key="", - environment=ArcmiraEnvironment.DEFAULT, -) - -client.transcripts.merges.list( - video_id="video_id", -) +**id:** `str` — Monitor id. -``` -
-
-#### ⚙️ Parameters - -
-
-
-**video_id:** `str` — YouTube video id, 11 characters. +**limit:** `typing.Optional[int]` — Alerts to return, newest first, 1 to 100. Default 25.
@@ -8968,7 +3121,8 @@ client.transcripts.merges.list(
-
client.transcripts.merges.submit(...) -> VideoMergeSubmittedResponse +## Monitors Entities +
client.monitors.entities.add(...) -> MonitorAddEntitiesResponse
@@ -8980,7 +3134,7 @@ client.transcripts.merges.list(
-Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows). +Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes.
@@ -9003,11 +3157,12 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.merges.submit( - video_id="video_id", +client.monitors.entities.add( + id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - source_name="sourceName", - target_entity_id=1, + entity_ids=[ + "entity_ids" + ], ) ``` @@ -9024,7 +3179,7 @@ client.transcripts.merges.submit(
-**video_id:** `str` — YouTube video id, 11 characters. +**id:** `str` — Monitor id.
@@ -9032,7 +3187,7 @@ client.transcripts.merges.submit(
-**source_name:** `str` — The name as it appears in this video (e.g. a first-name-only mention). +**entity_ids:** `typing.List[str]` — Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity.
@@ -9040,7 +3195,7 @@ client.transcripts.merges.submit(
-**target_entity_id:** `int` — The canonical entity these mentions actually refer to. +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
@@ -9048,7 +3203,7 @@ client.transcripts.merges.submit(
-**idempotency_key:** `typing.Optional[str]` — 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. +**person_match_mode:** `typing.Optional[AddEntitiesRequestPersonMatchMode]` — For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}).
@@ -9056,37 +3211,36 @@ client.transcripts.merges.submit(
-**replace_with:** `typing.Optional[str]` — Optional respelling applied to the transcript text (e.g. "Imad" → "Emad"). +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
+ +
-
-
- -**revision:** `typing.Optional[str]`
+
+## Trackers Alerts +
client.trackers.alerts.list(...) -> AlertListResponse
-**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. +#### 📝 Description -
-
- - +
+
+
+
+The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. +
+
-
- -
client.transcripts.merges.withdraw(...) -> WithdrawnResponse -
-
#### 🔌 Usage @@ -9105,8 +3259,7 @@ client = Arcmira( environment=ArcmiraEnvironment.DEFAULT, ) -client.transcripts.merges.withdraw( - video_id="video_id", +client.trackers.alerts.list( id="id", ) @@ -9124,7 +3277,7 @@ client.transcripts.merges.withdraw(
-**video_id:** `str` — YouTube video id, 11 characters. +**id:** `str` — Tracker id, trk_ form.
@@ -9132,7 +3285,7 @@ client.transcripts.merges.withdraw(
-**id:** `str` — The pending row id, as result.id of the response that accepted it. +**limit:** `typing.Optional[int]` — Alerts to return, newest first, 1 to 100. Default 25.
diff --git a/scripts/install-generated.py b/scripts/install-generated.py index 79dacbf..74b63e0 100644 --- a/scripts/install-generated.py +++ b/scripts/install-generated.py @@ -17,22 +17,11 @@ generated_error = (target / 'core/api_error.py').read_text() assert 'class ApiError(Exception)' in generated_error (target / 'core/api_error.py').write_text((root / 'scripts/overrides/api_error.py').read_text()) -# prepare_and_wait is hand-written; the generated transcripts clients inherit its mixins. -(target / 'transcripts/prepare.py').write_text((root / 'scripts/overrides/prepare.py').read_text()) -transcripts_client = target / 'transcripts/client.py' -text = transcripts_client.read_text() -for name, mixin in (('TranscriptsClient', 'PrepareAndWait'), ('AsyncTranscriptsClient', 'AsyncPrepareAndWait')): - declaration = f'class {name}:\n' - assert text.count(declaration) == 1 - text = text.replace(declaration, f'class {name}({mixin}):\n') -anchor_import = 'from .raw_client import' -assert text.count(anchor_import) == 1 -transcripts_client.write_text(text.replace(anchor_import, 'from .prepare import AsyncPrepareAndWait, PrepareAndWait\n' + anchor_import)) # The previous public pointer package exported these constants. init = target / '__init__.py' text = init.read_text() assert text.startswith('# This file was auto-generated by Fern') -init.write_text(text + '\nfrom ._package import __version__, homepage, docs, api_base, openapi, llms_txt, docs_llms_txt\nfrom .transcripts.prepare import PreparationError, PreparationFailedError, PreparationTimeoutError, PremiumUnavailableError\n') +init.write_text(text + '\nfrom ._package import __version__, homepage, docs, api_base, openapi, llms_txt, docs_llms_txt\n') version = (root / 'VERSION').read_text().strip() (target / '_package.py').write_text(f'''# Written by scripts/install-generated.py from VERSION. __version__ = {version!r} diff --git a/scripts/overrides/prepare.py b/scripts/overrides/prepare.py deleted file mode 100644 index 2d0770a..0000000 --- a/scripts/overrides/prepare.py +++ /dev/null @@ -1,179 +0,0 @@ -# Maintained by hand. scripts/install-generated.py copies this to -# src/arcmira/transcripts/prepare.py and makes the generated transcripts -# clients inherit these mixins, so regeneration keeps prepare_and_wait. - -from __future__ import annotations - -import asyncio -import time -import typing -import uuid - -from ..types.transcript_job import TranscriptJob -from ..types.transcript_result import TranscriptResult_Ready - -if typing.TYPE_CHECKING: - from .client import AsyncTranscriptsClient, TranscriptsClient - -DEFAULT_POLL_SECONDS = 5 - - -class PreparationError(Exception): - """Base class for prepare_and_wait refusals that are not HTTP errors.""" - - -class PreparationTimeoutError(PreparationError): - """The Premium job was still pending when timeout_seconds ran out. - - The job keeps running. Read the transcript later, or poll `job.status_url`. - """ - - def __init__(self, job: TranscriptJob) -> None: - super().__init__(f"Premium job {job.id} for {job.video_id} is still {job.status}") - self.job = job - - -class PreparationFailedError(PreparationError): - """The Premium job ended failed or refunded, or finished without a servable transcript.""" - - def __init__(self, job: TranscriptJob) -> None: - super().__init__(f"Premium job {job.id} for {job.video_id} ended {job.state}: {job.error or job.status}") - self.job = job - - -class PremiumUnavailableError(PreparationError): - """The account was served captions instead of Premium, so nothing can be prepared.""" - - def __init__(self, transcript: TranscriptResult_Ready) -> None: - super().__init__(f"Premium is not available for this account; the read returned {transcript.quality}") - self.transcript = transcript - - -# One step of the plan. The sync and async clients run the same plan, so -# they cannot drift on when to submit, when to poll, or when to give up. -_READ, _SUBMIT, _STATUS, _SLEEP = "read", "submit", "status", "sleep" -_Step = typing.Tuple[str, typing.Any] - - -def _delay(response: typing.Any, job: TranscriptJob) -> float: - header = response.response.headers.get("retry-after") - if header is not None and header.strip().isdigit(): - return float(header) - return float(job.next_poll_seconds if job.next_poll_seconds is not None else DEFAULT_POLL_SECONDS) - - -def _owned(transcript: typing.Any) -> TranscriptResult_Ready: - if transcript.quality != "premium": - raise PremiumUnavailableError(transcript) - return transcript - - -def _plan( - video_id: str, max_on_demand_cents: int, timeout_seconds: float -) -> typing.Generator[_Step, typing.Any, TranscriptResult_Ready]: - if max_on_demand_cents < 0: - raise ValueError("max_on_demand_cents cannot be negative") - deadline = time.monotonic() + timeout_seconds - read = yield (_READ, None) - transcript = read.data - if transcript.state == "ready": - return _owned(transcript) - if transcript.state == "pending": - response, job = read, transcript.job - else: - submission: typing.Dict[str, typing.Any] = {"video_id": video_id} - if max_on_demand_cents > 0: - # Spending money is the only case that needs a key; one key serves this call and its own retries. - submission.update(max_on_demand_cents=max_on_demand_cents, idempotency_key=str(uuid.uuid4())) - if transcript.quote is not None: - submission["max_rows"] = transcript.quote.rows - response = yield (_SUBMIT, submission) - job = response.data.job - while job.state == "pending": - remaining = deadline - time.monotonic() - if remaining <= 0: - raise PreparationTimeoutError(job) - yield (_SLEEP, min(_delay(response, job), remaining)) - response = yield (_STATUS, job.id) - job = response.data - if job.state != "ready": - raise PreparationFailedError(job) - final = (yield (_READ, None)).data - if final.state != "ready": - raise PreparationFailedError(job) - return _owned(final) - - -class PrepareAndWait: - def prepare_and_wait( - self: TranscriptsClient, - video_id: str, - *, - max_on_demand_cents: int = 0, - timeout_seconds: float = 300, - ) -> TranscriptResult_Ready: - """ - Read the Premium transcript for a video, preparing it first when the account does not own it. - - Reads `quality="premium"`. A ready transcript is returned as is. When preparation is required, - this posts `{video_id}` once, polls the job at the pace the API asks for, and returns the - transcript when it is ready. The default spends included credits only and moves no money. - A positive `max_on_demand_cents` authorizes that many cents of on-demand spend, and the call - sends an Idempotency-Key and the quoted `max_rows`. - - Raises `PreparationTimeoutError` (carrying the job) when `timeout_seconds` pass first, - `PreparationFailedError` when the job fails or is refunded, and `PremiumUnavailableError` - when the plan has no Premium. API refusals raise `ApiError`. - """ - plan = _plan(video_id, max_on_demand_cents, timeout_seconds) - raw = self.with_raw_response - try: - step = next(plan) - while True: - kind, argument = step - if kind == _SLEEP: - time.sleep(argument) - reply = None - elif kind == _READ: - reply = raw.get(video_id, quality="premium") - elif kind == _SUBMIT: - reply = raw.request(**argument) - else: - reply = raw.status(argument) - step = plan.send(reply) - except StopIteration as done: - return done.value - - -class AsyncPrepareAndWait: - async def prepare_and_wait( - self: AsyncTranscriptsClient, - video_id: str, - *, - max_on_demand_cents: int = 0, - timeout_seconds: float = 300, - ) -> TranscriptResult_Ready: - """ - Read the Premium transcript for a video, preparing it first when the account does not own it. - - The asynchronous twin of `Arcmira.transcripts.prepare_and_wait`; it takes the same arguments, - follows the same plan and raises the same errors. - """ - plan = _plan(video_id, max_on_demand_cents, timeout_seconds) - raw = self.with_raw_response - try: - step = next(plan) - while True: - kind, argument = step - if kind == _SLEEP: - await asyncio.sleep(argument) - reply = None - elif kind == _READ: - reply = await raw.get(video_id, quality="premium") - elif kind == _SUBMIT: - reply = await raw.request(**argument) - else: - reply = await raw.status(argument) - step = plan.send(reply) - except StopIteration as done: - return done.value diff --git a/scripts/prepare-openapi.py b/scripts/prepare-openapi.py index 365111b..47ef630 100644 --- a/scripts/prepare-openapi.py +++ b/scripts/prepare-openapi.py @@ -1,10 +1,24 @@ -"""Build Fern's public generation input without changing the HTTP contract.""" +"""Build Fern's public generation input without changing the HTTP contract. + +fern/method-names.json names the SDK group and method of every operation, keyed by operationId. +An operation it does not name, or a name for an operation the document lacks, fails the build, +so a new route gets an SDK name on purpose. +""" import copy import json -import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] +METHODS = {'get', 'put', 'post', 'patch', 'delete'} +# Account bootstrap and the document itself are not SDK calls. +EXCLUDED = {'get_openapi_document', 'create_signup', 'verify_signup'} +# The product noun is transcripts, so the Job a Premium read returns is a TranscriptJob. +TYPE_NAMES = { + 'TranscriptionJob': 'TranscriptJob', + 'TranscriptionListResponse': 'TranscriptRequestListResponse', +} +# The one array property of each paged success body. +COLLECTIONS = {'mentions', 'recommendations', 'alerts', 'episodes', 'requests'} def resolve(document, schema): @@ -21,85 +35,79 @@ def resolve(document, schema): def collection(document, schema): props = resolve(document, schema).get('properties', {}) arrays = [key for key, value in props.items() if resolve(document, value).get('type') == 'array'] - if len(arrays) != 1 or arrays[0] not in {'data', 'requests', 'episodes', 'items'}: + if len(arrays) != 1 or arrays[0] not in COLLECTIONS: raise ValueError(f'Unknown or ambiguous cursor collection: {arrays}') return arrays[0] +def walk(value, visit): + if isinstance(value, dict): + visit(value) + for child in value.values(): + walk(child, visit) + elif isinstance(value, list): + for child in value: + walk(child, visit) + + def prepare(document, names): doc = copy.deepcopy(document) - for path in ('/v1/openapi.json', '/v1/signups', '/v1/signups/verify', '/v1/search'): - del doc['paths'][path] - type_names = { - 'TranscriptionJob': 'TranscriptJob', - 'TranscriptionSubmitResponse': 'TranscriptRequestSubmitResponse', - 'TranscriptionListResponse': 'TranscriptRequestListResponse', - } schemas = doc['components']['schemas'] - for source, target in type_names.items(): + for source, target in TYPE_NAMES.items(): schemas[target] = schemas.pop(source) - def rename_refs(value): - if isinstance(value, dict): - ref = value.get('$ref', '') - source = ref.removeprefix('#/components/schemas/') - if source in type_names: - value['$ref'] = '#/components/schemas/' + type_names[source] - for child in value.values(): rename_refs(child) - elif isinstance(value, list): - for child in value: rename_refs(child) - rename_refs(doc) - def collapse_job_refs(value): - # Fern inlines an allOf of the Job $ref plus a description; a bare $ref keeps one shared Job type. - if isinstance(value, dict): - parts = value.get('allOf') - if parts and sum('$ref' in part for part in parts) == 1 and all('$ref' in part or set(part) == {'description'} for part in parts) and any(part.get('$ref') == '#/components/schemas/TranscriptJob' for part in parts): - del value['allOf'] - value['$ref'] = next(part['$ref'] for part in parts if '$ref' in part) - for child in value.values(): collapse_job_refs(child) - elif isinstance(value, list): - for child in value: collapse_job_refs(child) - collapse_job_refs(doc) - # Fern 5.131.1 loses inherited example fields in this object intersection. - suggestion = doc['components']['schemas']['ResolveSuggestion'] + + def rename_ref(value): + source = value.get('$ref', '').removeprefix('#/components/schemas/') + if source in TYPE_NAMES: + value['$ref'] = '#/components/schemas/' + TYPE_NAMES[source] + + def collapse_described_ref(value): + # Fern inlines an allOf of one $ref plus a description; a bare $ref keeps one shared type. + parts = value.get('allOf') + if parts and sum('$ref' in part for part in parts) == 1 and all('$ref' in part or set(part) == {'description'} for part in parts): + del value['allOf'] + value['$ref'] = next(part['$ref'] for part in parts if '$ref' in part) + + walk(doc, rename_ref) + walk(doc, collapse_described_ref) + # Fern 5.131.1 loses inherited example fields in this object intersection. The flat object stays + # nullable: resolve answers suggested: null whenever it has a best match. + suggestion = schemas['ResolveSuggestion'] members = [resolve(doc, part) for part in suggestion.pop('allOf')] - suggestion.update(type='object', properties={}, required=[]) + suggestion.update(type=['object', 'null'], properties={}, required=[]) for member in members: suggestion['properties'].update(member['properties']) suggestion['required'].extend(member.get('required', [])) doc['servers'] = [{'url': 'https://api.arcmira.com'}] doc['components']['securitySchemes']['bearerAuth']['x-fern-bearer'] = {'name': 'apiKey', 'env': 'ARCMIRA_API_KEY'} - special = { - 'quote_transcription': ('transcripts', 'quote'), - 'submit_transcription': ('transcripts', 'request'), - 'get_transcription': ('transcripts', 'status'), - 'list_transcriptions': ('transcripts', 'listRequests'), - } - for path, methods in doc['paths'].items(): - for method, op in methods.items(): - if method not in {'get', 'post', 'put', 'patch', 'delete'}: + + unnamed = [] + for path in list(doc['paths']): + methods = doc['paths'][path] + for method in [m for m in methods if m in METHODS]: + op = methods[method] + operation_id = op['operationId'] + if operation_id in EXCLUDED: + del methods[method] + continue + if operation_id not in names: + unnamed.append(operation_id) continue + op['x-fern-sdk-group-name'] = names[operation_id]['group'] + op['x-fern-sdk-method-name'] = names[operation_id]['method'] op['parameters'] = [p for p in op.get('parameters', []) if not (p.get('in') == 'query' and p['name'] == 'src')] - key = method + ' ' + re.sub(r'\{[^}]+\}', '{}', path) - if op.get('operationId') in special: - group, name = special[op['operationId']] - op['x-fern-sdk-group-name'] = group - op['x-fern-sdk-method-name'] = name - elif key in names: - op['x-fern-sdk-group-name'] = names[key]['group'] - op['x-fern-sdk-method-name'] = names[key]['method'] - if path == '/v1/feedback' and method == 'post': - # Legacy query alternatives collide with the established body API. - op['parameters'] = [p for p in op.get('parameters', []) if not (p.get('in') == 'query' and p['name'] in {'type', 'query'})] - body = op['requestBody']['content']['application/json']['schema'] - body['required'] = sorted(set(body.get('required', [])) | {'type', 'query'}) - if op.get('operationId') == 'submit_transcription': - # videoId is a one-release alias of video_id and collides with it after camelCase normalization. - op['requestBody']['content']['application/json']['schema']['properties'].pop('videoId') + body = resolve(doc, op.get('requestBody', {})).get('content', {}).get('application/json', {}).get('schema', {}) + # A deprecated body alias collides with its canonical name after camelCase normalization. + for name in [name for name, prop in body.get('properties', {}).items() if prop.get('deprecated')]: + del body['properties'][name] + if operation_id == 'submit_feedback': + # type and query are also read from the query string as a curl convenience; the SDKs send the body. + op['parameters'] = [p for p in op['parameters'] if not (p.get('in') == 'query' and p['name'] in {'type', 'query'})] + body['required'] = sorted(set(body.get('required', [])) | {'type'}) responses = [] for code, response in op.get('responses', {}).items(): if code.startswith('2'): - response = resolve(doc, response) - schema = response.get('content', {}).get('application/json', {}).get('schema') + schema = resolve(doc, response).get('content', {}).get('application/json', {}).get('schema') for member in (schema or {}).get('oneOf', [schema] if schema is not None else []): if member not in responses: responses.append(member) @@ -110,18 +118,26 @@ def collapse_job_refs(value): if len(state) != 1 or state[0] in states or '$ref' not in schema: raise ValueError(f'Cannot discriminate success schemas for {method} {path}') states[state[0]] = schema['$ref'] - union_name = 'TranscriptResult' if op['operationId'] == 'get_transcript' else op['operationId'] + 'Result' - doc['components']['schemas'][union_name] = {'oneOf': responses, 'discriminator': {'propertyName': 'state', 'mapping': states}} + union_name = 'TranscriptResult' if operation_id == 'get_transcript' else operation_id + 'Result' + schemas[union_name] = {'oneOf': responses, 'discriminator': {'propertyName': 'state', 'mapping': states}} for code, response in op['responses'].items(): if code.startswith('2'): response['content']['application/json']['schema'] = {'$ref': '#/components/schemas/' + union_name} - if method == 'get' and any(p.get('name') == 'cursor' and p.get('in') == 'query' for p in op.get('parameters', [])): + if method == 'get' and any(p.get('name') == 'cursor' and p.get('in') == 'query' for p in op['parameters']): if len(responses) != 1: raise ValueError(f'Cursor operation lacks a single collection response: {path}') items = collection(doc, responses[0]) if 'next_cursor' not in resolve(doc, responses[0]).get('properties', {}): raise ValueError(f'Cursor operation lacks next_cursor: {path}') op['x-fern-pagination'] = {'cursor': '$request.cursor', 'next_cursor': '$response.next_cursor', 'results': '$response.' + items} + if not any(m in METHODS for m in methods): + del doc['paths'][path] + if unnamed: + raise ValueError(f'fern/method-names.json names no SDK method for: {", ".join(unnamed)}') + present = {op['operationId'] for methods in document['paths'].values() for m, op in methods.items() if m in METHODS} + stale = sorted(set(names) - present) + if stale: + raise ValueError(f'fern/method-names.json names operations the document no longer has: {", ".join(stale)}') return doc diff --git a/src/arcmira/__init__.py b/src/arcmira/__init__.py index 5ad0435..feb8597 100644 --- a/src/arcmira/__init__.py +++ b/src/arcmira/__init__.py @@ -18,39 +18,15 @@ ChannelCoverageResponse, ChannelCoverageResponseChannel, ChannelCoverageResponseChannelSourceMix, - ChannelGuestListResponse, - ChannelGuestListResponseExportCapabilities, - ChannelGuestListResponseItemsItem, - ChannelGuestListResponseItemsItemSentiment, - ChannelPageResponse, - ChannelPageResponseChannelInfo, - ChannelPageResponseEntity, - ChannelPageResponseEntityOwner, - ChannelPageResponseEntityType, - ChannelPageResponseEpisodesByMonthItem, - ChannelPageResponseEpisodesItem, - ChannelPageResponseEpisodesItemPlatform, - ChannelPageResponseEpisodesItemSentiment, - ChannelPageResponseEpisodesItemTimestamp, - ChannelPageResponseEpisodesItemType, - ChannelPageResponseGuestsItem, - ChannelPageResponseGuestsItemRole, - ChannelPageResponseGuestsItemSentiment, - ChannelPageResponseHostsDetailedItem, - ChannelPageResponseHostsDetailedItemSentiment, - ChannelPageResponseOrganizationsItem, - ChannelPageResponseOrganizationsItemSentiment, - ChannelPageResponseProductsItem, - ChannelPageResponseProductsItemSentiment, - ChannelPageResponseRecommendationsSummary, - ChannelPageResponseStats, - ChannelPageResponseTopicsItem, - ChannelPageResponseTopicsItemSentiment, ChannelSponsor, - ChannelSponsorEntity, ChannelSponsorSponsorStatus, ChannelSponsorsResponse, ChannelSponsorsResponseAccess, + ChannelSponsorsResponseAccessDetails, + ChannelSponsorsResponseAccessDetailsQuote, + ChannelSponsorsResponseAccessDetailsQuoteCharge, + ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, + ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, ChannelSponsorsResponseAccessGate, ChannelSponsorsResponseAccessReason, ChannelSponsorsResponseAccessType, @@ -61,21 +37,18 @@ ChannelVideosResponse, ChannelVideosResponseChannel, ChannelVideosResponseEpisodesItem, - CorrectionAcceptedResponse, - CorrectionAcceptedResponseKind, DeliveryIssueChange, DeliveryIssueChangeChannel, Entity, - EntityCard, - EntityCardsResponse, - EntityChannelListResponse, - EntityChannelListResponseExportCapabilities, - EntityChannelListResponseItemsItem, EntityDetailRecommendationsSummary, EntityDetailResponse, - EntityLookupResponse, EntityMomentumResponse, EntityMomentumResponseAccess, + EntityMomentumResponseAccessDetails, + EntityMomentumResponseAccessDetailsQuote, + EntityMomentumResponseAccessDetailsQuoteCharge, + EntityMomentumResponseAccessDetailsQuoteChargeFrom, + EntityMomentumResponseAccessDetailsQuoteChargeUnit, EntityMomentumResponseAccessGate, EntityMomentumResponseAccessReason, EntityMomentumResponseAccessType, @@ -85,48 +58,23 @@ EntityMomentumResponseTopShowsItem, EntityMomentumResponseVerdict, EntityMomentumResponseVolume, - EntityOrganizationListResponse, - EntityOrganizationListResponseExportCapabilities, - EntityOrganizationListResponseItemsItem, - EntityOrganizationListResponseItemsItemSentiment, - EntityPageMention, - EntityPageMentionExcerpt, - EntityPageMentionExcerptPublicSourceClass, - EntityPageMentionPlatform, - EntityPageMentionSentiment, - EntityPageMentionType, - EntityPeopleListResponse, - EntityPeopleListResponseExportCapabilities, - EntityPeopleListResponseItemsItem, - EntityPeopleListResponseItemsItemSentiment, - EntityPeopleListResponsePeopleMode, - EntityProductListResponse, - EntityProductListResponseExportCapabilities, - EntityProductListResponseItemsItem, - EntityProductListResponseItemsItemSentiment, EntityRef, EntityResolveResponse, EntityResolveResponseAsk, EntityResolveResponseAskOptionsItem, EntityResolveResponseConfidence, - EntitySearchResponse, - EntitySearchResult, - EntitySearchResultRecommendationsSummary, - EntityTopicListResponse, - EntityTopicListResponseExportCapabilities, - EntityTopicListResponseItemsItem, - EntityTopicListResponseItemsItemSentiment, Error, ErrorError, + ErrorErrorDetails, + ErrorErrorDetailsQuote, + ErrorErrorDetailsQuoteCharge, + ErrorErrorDetailsQuoteChargeFrom, + ErrorErrorDetailsQuoteChargeUnit, ErrorErrorGate, ErrorErrorReason, ErrorErrorType, ErrorErrorUnlock, ErrorErrorUnlockAction, - ErrorQuote, - ErrorQuoteCharge, - ErrorQuoteChargeFrom, - ErrorQuoteChargeUnit, ErrorResource, ErrorResourceChart, ErrorResourceCommercial, @@ -159,45 +107,7 @@ ErrorResource_Requests, ErrorResource_Rows, ErrorResource_SidebarRows, - ExposureMeta, - ExposureMetaAccess, - ExposureMetaAccessChart, - ExposureMetaAccessCls, - ExposureMetaAccessFreshness, - ExposureMetaAccessLadder, - ExposureMetaAccessRows, - ExposureMetaAccessRowsEntities, - ExposureMetaAccessRowsMedia, - ExposureMetaAccessRowsTopics, - ExposureMetaAccessUnlock, - ExposureMetaAccessUnlockLimitAction, - ExposureMetaAccessUnlockSrc, - ExposureMetaAccessView, - ExposureMetaAccessWithheldItem, - ExposureMetaAccessWithheldItemKind, - ExposureMetaAccessWithheldItemParam, - ExposureMetaAccessWithheldItemSection, - ExposureMetaAccessWithheldItemWhat, - ExposureMetaCredits, - ExposureMetaCreditsOnDemand, - ExposureMetaCreditsPlan, - ExposureMetaFreeLimit, - ExposureMetaLimitAction, - ExposureMetaLimits, - ExposureMetaRecentPreview, - ExposureMetaRecentPreviewExperiment, - ExposureMetaRecentPreviewMentions, - ExposureMetaRecentPreviewMentionsExperiment, - ExposureMetaRecentPreviewMentionsSubject, - ExposureMetaRecentPreviewMentionsTeaserItemsItem, - ExposureMetaRecentPreviewSubject, - ExposureMetaRecentPreviewTeaserItemsItem, - ExposureMetaTotals, - ExposureMetaUsageLimitType, FeedbackCorrectionResult, - FeedbackCorrectionResultRecommendation, - FeedbackCorrectionResultRecommendationMedia, - FeedbackCorrectionResultRecommendationMediaSourceChannel, FeedbackCorrectionResultStatus, FeedbackReadbackCorrection, FeedbackReadbackCorrectionStatus, @@ -223,7 +133,6 @@ MentionCountsResponseSharedItem, MentionCountsResponseSharedItemByChannelItem, MentionListResponse, - MentionListResponseEntity, MentionListResponseUnlock, MentionMedia, MentionMediaSourceChannel, @@ -234,16 +143,22 @@ MissedAlertChange, MissingResultChange, Monitor, + MonitorAccess, + MonitorAddEntitiesResponse, MonitorAddTrackersResponse, MonitorDeleteResponse, MonitorEmailRecipientsItem, MonitorEmailRecipientsItemInvitationStatus, + MonitorEmailRecipientsItemRole, MonitorEmailRecipientsItemStatus, + MonitorEntityResult, + MonitorEntityResultReason, MonitorListResponse, MonitorListResponseMonitorsItem, MonitorListResponseMonitorsItemSlackIntegration, MonitorMutationResponse, MonitorMutationResponseMonitor, + MonitorTeam, MonitorTrackersResponse, MonitorTrackersResponseTrackersItem, NamedEntityRef, @@ -251,80 +166,12 @@ OpenApiDocumentInfo, OpenApiDocumentInfoContact, OpenApiDocumentServersItem, - OrganizationPageResponse, - OrganizationPageResponseChannelsItem, - OrganizationPageResponseChannelsItemSentiment, - OrganizationPageResponseEntity, - OrganizationPageResponseEntityOwnedChannelsItem, - OrganizationPageResponseEntityOwnedProductsItem, - OrganizationPageResponseEntityType, - OrganizationPageResponseMentionsByMonthItem, - OrganizationPageResponsePeopleItem, - OrganizationPageResponsePeopleItemSentiment, - OrganizationPageResponseProductsItem, - OrganizationPageResponseProductsItemSentiment, - OrganizationPageResponseRoleEdge, - OrganizationPageResponseRoleEdgeLabel, - OrganizationPageResponseRoleEdgePeopleItem, - OrganizationPageResponseRoleEdgeRecentAppearancesItem, - OrganizationPageResponseRoleEdgeRole, - OrganizationPageResponseStats, - OrganizationPageResponseTopicsItem, - OrganizationPageResponseTopicsItemSentiment, - PersonAppearanceListResponse, - PersonAppearanceListResponseItemsItem, - PersonAppearanceListResponseItemsItemPlatform, - PersonAppearanceListResponseItemsItemSentiment, - PersonAppearanceListResponseItemsItemType, - PersonPageResponse, - PersonPageResponseAppearancesByMonthItem, - PersonPageResponseAppearancesItem, - PersonPageResponseAppearancesItemPlatform, - PersonPageResponseAppearancesItemSentiment, - PersonPageResponseAppearancesItemType, - PersonPageResponseBrandsItem, - PersonPageResponseBrandsItemSentiment, - PersonPageResponseEntity, - PersonPageResponseEntityOwnedChannelsItem, - PersonPageResponseEntityOwnedProductsItem, - PersonPageResponseMentionsByMonthItem, - PersonPageResponseMentionsItem, - PersonPageResponseMentionsItemPlatform, - PersonPageResponseMentionsItemSentiment, - PersonPageResponseMentionsItemType, - PersonPageResponsePeopleItem, - PersonPageResponsePeopleItemRole, - PersonPageResponsePeopleItemSentiment, - PersonPageResponseProductsItem, - PersonPageResponseProductsItemSentiment, - PersonPageResponseRoleEdge, - PersonPageResponseRoleEdgeLabel, - PersonPageResponseRoleEdgeRole, - PersonPageResponseStats, - PersonPageResponseTopicsItem, - PersonPageResponseTopicsItemSentiment, - ProductPageResponse, - ProductPageResponseChannelsItem, - ProductPageResponseChannelsItemSentiment, - ProductPageResponseEntity, - ProductPageResponseEntityOwner, - ProductPageResponseEntityParentOrg, - ProductPageResponseEntityType, - ProductPageResponseMentionsByMonthItem, - ProductPageResponseOpportunities, - ProductPageResponseOrganizationsItem, - ProductPageResponseOrganizationsItemSentiment, - ProductPageResponsePeopleItem, - ProductPageResponsePeopleItemSentiment, - ProductPageResponseStats, - ProductPageResponseTopicsItem, - ProductPageResponseTopicsItemSentiment, - PublishedExcerpt, - PublishedExcerptPublicSourceClass, + PublicationWindow, Recommendation, + RecommendationClass, RecommendationEnrichmentItem, + RecommendationEnrichmentItemClass, RecommendationListResponse, - RecommendationListResponseEntity, RecommendationMedia, RecommendationMediaSourceChannel, ResolveCandidate, @@ -332,50 +179,17 @@ ResolveSuggestion, ResolveSuggestionMatch, ResolveSuggestionReason, - SearchResolveResponse, - SearchResolveResponseEntity, SignupSentResponse, SignupSentResponseNext, SignupSentResponseNextMethod, SignupVerifiedResponse, - SpeakerIdentificationSubmittedResponse, - SpeakerIdentificationSubmittedResponseIdentification, - SpeakerIdentificationSubmittedResponseIdentificationEntity, - SpeakerIdentificationSubmittedResponseIdentificationStatus, + SlackIntegrationListResponse, + SlackIntegrationListResponseIntegrationsItem, + SlackIntegrationListResponseIntegrationsItemChannelsItem, StaleMetadataChange, - TeamMember, - TeamMemberRole, - TeamMemberSeatType, - TeamMemberSpend, - TeamMemberSpendRole, - TeamMemberSpendSeatType, - TeamMembersResponse, - TeamMembersResponseTeam, - TeamSpendResponse, - TeamUsageEvent, - TeamUsageEventsResponse, - TopicPageResponse, - TopicPageResponseChannelsItem, - TopicPageResponseChannelsItemSentiment, - TopicPageResponseCompaniesItem, - TopicPageResponseCompaniesItemSentiment, - TopicPageResponseEntity, - TopicPageResponseEntityType, - TopicPageResponseMentionsByMonthItem, - TopicPageResponseProductsItem, - TopicPageResponseProductsItemSentiment, - TopicPageResponseRelatedTopicsItem, - TopicPageResponseRelatedTopicsItemSentiment, - TopicPageResponseStats, - TopicPageResponseVoicesItem, - TopicPageResponseVoicesItemRole, - TopicPageResponseVoicesItemSentiment, Tracker, TrackerListResponse, TrackerMutationResponse, - TranscriptEditSubmittedResponse, - TranscriptEditSubmittedResponseEdit, - TranscriptEditSubmittedResponseEditStatus, TranscriptJob, TranscriptJobCharge, TranscriptJobChargeFrom, @@ -385,16 +199,6 @@ TranscriptJobStatus, TranscriptPending, TranscriptPendingQuality, - TranscriptPreparationRequired, - TranscriptPreparationRequiredAction, - TranscriptPreparationRequiredActionBody, - TranscriptPreparationRequiredActionMethod, - TranscriptPreparationRequiredLastAttempt, - TranscriptPreparationRequiredQuality, - TranscriptPreparationRequiredQuote, - TranscriptPreparationRequiredQuoteCharge, - TranscriptPreparationRequiredQuoteChargeFrom, - TranscriptPreparationRequiredQuoteChargeUnit, TranscriptPurchaseQuote, TranscriptPurchaseQuoteBillingScope, TranscriptPurchaseQuoteCharge, @@ -404,9 +208,13 @@ TranscriptQuote, TranscriptRequestListResponse, TranscriptRequestListResponseRequestsItem, - TranscriptRequestSubmitResponse, TranscriptResponse, TranscriptResponseAccess, + TranscriptResponseAccessDetails, + TranscriptResponseAccessDetailsQuote, + TranscriptResponseAccessDetailsQuoteCharge, + TranscriptResponseAccessDetailsQuoteChargeFrom, + TranscriptResponseAccessDetailsQuoteChargeUnit, TranscriptResponseAccessGate, TranscriptResponseAccessReason, TranscriptResponseAccessType, @@ -420,33 +228,31 @@ TranscriptResponseSpeakersItem, TranscriptResult, TranscriptResult_Pending, - TranscriptResult_PreparationRequired, TranscriptResult_Ready, TranscriptSearchChunk, TranscriptSearchResponse, TranscriptSearchResponseAccess, + TranscriptSearchResponseAccessDetails, + TranscriptSearchResponseAccessDetailsQuote, + TranscriptSearchResponseAccessDetailsQuoteCharge, + TranscriptSearchResponseAccessDetailsQuoteChargeFrom, + TranscriptSearchResponseAccessDetailsQuoteChargeUnit, TranscriptSearchResponseAccessGate, TranscriptSearchResponseAccessReason, TranscriptSearchResponseAccessType, TranscriptSearchResponseAccessUnlock, TranscriptSearchResponseAccessUnlockAction, TranscriptSearchResponseFilters, + TranscriptSearchResponseFiltersKindItem, TranscriptSearchResponseSearchIndex, TranscriptSearchResponseSearchIndexState, + TranscriptSearchResponseUnlock, TranscriptSettings, TranscriptSettingsQuality, TranscriptVideo, - VideoCaptionsResponse, - VideoMergeListResponse, - VideoMergeListResponseMergesItem, - VideoMergeListResponseMergesItemStatus, - VideoMergeSubmittedResponse, - VideoMergeSubmittedResponseMerge, - VideoMergeSubmittedResponseMergeStatus, WebhookSecretRotateResponse, - WithdrawnResponse, WrongClassificationChange, - WrongClassificationChangeMentionClass, + WrongClassificationChangeClass, WrongEntityChange, WrongEntityTypeChange, WrongEntityTypeChangeField, @@ -458,52 +264,41 @@ InternalServerError, NotFoundError, PaymentRequiredError, - PreconditionFailedError, ServiceUnavailableError, TooManyRequestsError, UnauthorizedError, ) from . import ( channels, - corrections, entities, feedback, health, + integrations, me, mentions, monitors, - organizations, - people, - products, recommendations, - team, - topics, trackers, transcripts, ) from ._default_clients import DefaultAioHttpClient, DefaultAsyncHttpxClient from .client import Arcmira, AsyncArcmira - from .corrections import SubmitCorrectionsRequestAnchor, SubmitCorrectionsRequestKind - from .entities import LookupEntitiesRequestType, ResolveEntitiesRequestType, SearchEntitiesRequestType + from .entities import ResolveEntitiesRequestType from .environment import ArcmiraEnvironment from .feedback import ( + SubmitFeedbackRequestCategory, SubmitFeedbackRequestCorrectionsItem, + SubmitFeedbackRequestCorrectionsItemClass, SubmitFeedbackRequestCorrectionsItemIssueType, - SubmitFeedbackRequestCorrectionsItemMentionClass, SubmitFeedbackRequestCorrectionsItemReason, SubmitFeedbackRequestCorrectionsItemSuggestedChange, SubmitFeedbackRequestMethod, SubmitFeedbackRequestType, ) from .me import UpdateSettingsMeRequestTranscripts, UpdateSettingsMeRequestTranscriptsQuality - from .mentions import ( - CountMentionsRequestMode, - ListMentionsRequestDetails, - ListMentionsRequestEntityType, - ListMentionsRequestSentiment, - ) + from .mentions import CountMentionsRequestMode, ListMentionsRequestDetails, ListMentionsRequestSentiment from .monitors import CreateMonitorsRequestNotifyFrequency, UpdateMonitorsRequestNotifyFrequency - from .recommendations import ListRecommendationsRequestEntityType, ListRecommendationsRequestMentionClass + from .recommendations import ListRecommendationsRequestClass from .trackers import ( CreateTrackersRequestEntityType, CreateTrackersRequestPersonMatchMode, @@ -526,39 +321,15 @@ "ChannelCoverageResponse": ".types", "ChannelCoverageResponseChannel": ".types", "ChannelCoverageResponseChannelSourceMix": ".types", - "ChannelGuestListResponse": ".types", - "ChannelGuestListResponseExportCapabilities": ".types", - "ChannelGuestListResponseItemsItem": ".types", - "ChannelGuestListResponseItemsItemSentiment": ".types", - "ChannelPageResponse": ".types", - "ChannelPageResponseChannelInfo": ".types", - "ChannelPageResponseEntity": ".types", - "ChannelPageResponseEntityOwner": ".types", - "ChannelPageResponseEntityType": ".types", - "ChannelPageResponseEpisodesByMonthItem": ".types", - "ChannelPageResponseEpisodesItem": ".types", - "ChannelPageResponseEpisodesItemPlatform": ".types", - "ChannelPageResponseEpisodesItemSentiment": ".types", - "ChannelPageResponseEpisodesItemTimestamp": ".types", - "ChannelPageResponseEpisodesItemType": ".types", - "ChannelPageResponseGuestsItem": ".types", - "ChannelPageResponseGuestsItemRole": ".types", - "ChannelPageResponseGuestsItemSentiment": ".types", - "ChannelPageResponseHostsDetailedItem": ".types", - "ChannelPageResponseHostsDetailedItemSentiment": ".types", - "ChannelPageResponseOrganizationsItem": ".types", - "ChannelPageResponseOrganizationsItemSentiment": ".types", - "ChannelPageResponseProductsItem": ".types", - "ChannelPageResponseProductsItemSentiment": ".types", - "ChannelPageResponseRecommendationsSummary": ".types", - "ChannelPageResponseStats": ".types", - "ChannelPageResponseTopicsItem": ".types", - "ChannelPageResponseTopicsItemSentiment": ".types", "ChannelSponsor": ".types", - "ChannelSponsorEntity": ".types", "ChannelSponsorSponsorStatus": ".types", "ChannelSponsorsResponse": ".types", "ChannelSponsorsResponseAccess": ".types", + "ChannelSponsorsResponseAccessDetails": ".types", + "ChannelSponsorsResponseAccessDetailsQuote": ".types", + "ChannelSponsorsResponseAccessDetailsQuoteCharge": ".types", + "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom": ".types", + "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit": ".types", "ChannelSponsorsResponseAccessGate": ".types", "ChannelSponsorsResponseAccessReason": ".types", "ChannelSponsorsResponseAccessType": ".types", @@ -570,8 +341,6 @@ "ChannelVideosResponseChannel": ".types", "ChannelVideosResponseEpisodesItem": ".types", "ConflictError": ".errors", - "CorrectionAcceptedResponse": ".types", - "CorrectionAcceptedResponseKind": ".types", "CountMentionsRequestMode": ".mentions", "CreateMonitorsRequestNotifyFrequency": ".monitors", "CreateTrackersRequestEntityType": ".trackers", @@ -581,16 +350,15 @@ "DeliveryIssueChange": ".types", "DeliveryIssueChangeChannel": ".types", "Entity": ".types", - "EntityCard": ".types", - "EntityCardsResponse": ".types", - "EntityChannelListResponse": ".types", - "EntityChannelListResponseExportCapabilities": ".types", - "EntityChannelListResponseItemsItem": ".types", "EntityDetailRecommendationsSummary": ".types", "EntityDetailResponse": ".types", - "EntityLookupResponse": ".types", "EntityMomentumResponse": ".types", "EntityMomentumResponseAccess": ".types", + "EntityMomentumResponseAccessDetails": ".types", + "EntityMomentumResponseAccessDetailsQuote": ".types", + "EntityMomentumResponseAccessDetailsQuoteCharge": ".types", + "EntityMomentumResponseAccessDetailsQuoteChargeFrom": ".types", + "EntityMomentumResponseAccessDetailsQuoteChargeUnit": ".types", "EntityMomentumResponseAccessGate": ".types", "EntityMomentumResponseAccessReason": ".types", "EntityMomentumResponseAccessType": ".types", @@ -600,48 +368,23 @@ "EntityMomentumResponseTopShowsItem": ".types", "EntityMomentumResponseVerdict": ".types", "EntityMomentumResponseVolume": ".types", - "EntityOrganizationListResponse": ".types", - "EntityOrganizationListResponseExportCapabilities": ".types", - "EntityOrganizationListResponseItemsItem": ".types", - "EntityOrganizationListResponseItemsItemSentiment": ".types", - "EntityPageMention": ".types", - "EntityPageMentionExcerpt": ".types", - "EntityPageMentionExcerptPublicSourceClass": ".types", - "EntityPageMentionPlatform": ".types", - "EntityPageMentionSentiment": ".types", - "EntityPageMentionType": ".types", - "EntityPeopleListResponse": ".types", - "EntityPeopleListResponseExportCapabilities": ".types", - "EntityPeopleListResponseItemsItem": ".types", - "EntityPeopleListResponseItemsItemSentiment": ".types", - "EntityPeopleListResponsePeopleMode": ".types", - "EntityProductListResponse": ".types", - "EntityProductListResponseExportCapabilities": ".types", - "EntityProductListResponseItemsItem": ".types", - "EntityProductListResponseItemsItemSentiment": ".types", "EntityRef": ".types", "EntityResolveResponse": ".types", "EntityResolveResponseAsk": ".types", "EntityResolveResponseAskOptionsItem": ".types", "EntityResolveResponseConfidence": ".types", - "EntitySearchResponse": ".types", - "EntitySearchResult": ".types", - "EntitySearchResultRecommendationsSummary": ".types", - "EntityTopicListResponse": ".types", - "EntityTopicListResponseExportCapabilities": ".types", - "EntityTopicListResponseItemsItem": ".types", - "EntityTopicListResponseItemsItemSentiment": ".types", "Error": ".types", "ErrorError": ".types", + "ErrorErrorDetails": ".types", + "ErrorErrorDetailsQuote": ".types", + "ErrorErrorDetailsQuoteCharge": ".types", + "ErrorErrorDetailsQuoteChargeFrom": ".types", + "ErrorErrorDetailsQuoteChargeUnit": ".types", "ErrorErrorGate": ".types", "ErrorErrorReason": ".types", "ErrorErrorType": ".types", "ErrorErrorUnlock": ".types", "ErrorErrorUnlockAction": ".types", - "ErrorQuote": ".types", - "ErrorQuoteCharge": ".types", - "ErrorQuoteChargeFrom": ".types", - "ErrorQuoteChargeUnit": ".types", "ErrorResource": ".types", "ErrorResourceChart": ".types", "ErrorResourceCommercial": ".types", @@ -674,45 +417,7 @@ "ErrorResource_Requests": ".types", "ErrorResource_Rows": ".types", "ErrorResource_SidebarRows": ".types", - "ExposureMeta": ".types", - "ExposureMetaAccess": ".types", - "ExposureMetaAccessChart": ".types", - "ExposureMetaAccessCls": ".types", - "ExposureMetaAccessFreshness": ".types", - "ExposureMetaAccessLadder": ".types", - "ExposureMetaAccessRows": ".types", - "ExposureMetaAccessRowsEntities": ".types", - "ExposureMetaAccessRowsMedia": ".types", - "ExposureMetaAccessRowsTopics": ".types", - "ExposureMetaAccessUnlock": ".types", - "ExposureMetaAccessUnlockLimitAction": ".types", - "ExposureMetaAccessUnlockSrc": ".types", - "ExposureMetaAccessView": ".types", - "ExposureMetaAccessWithheldItem": ".types", - "ExposureMetaAccessWithheldItemKind": ".types", - "ExposureMetaAccessWithheldItemParam": ".types", - "ExposureMetaAccessWithheldItemSection": ".types", - "ExposureMetaAccessWithheldItemWhat": ".types", - "ExposureMetaCredits": ".types", - "ExposureMetaCreditsOnDemand": ".types", - "ExposureMetaCreditsPlan": ".types", - "ExposureMetaFreeLimit": ".types", - "ExposureMetaLimitAction": ".types", - "ExposureMetaLimits": ".types", - "ExposureMetaRecentPreview": ".types", - "ExposureMetaRecentPreviewExperiment": ".types", - "ExposureMetaRecentPreviewMentions": ".types", - "ExposureMetaRecentPreviewMentionsExperiment": ".types", - "ExposureMetaRecentPreviewMentionsSubject": ".types", - "ExposureMetaRecentPreviewMentionsTeaserItemsItem": ".types", - "ExposureMetaRecentPreviewSubject": ".types", - "ExposureMetaRecentPreviewTeaserItemsItem": ".types", - "ExposureMetaTotals": ".types", - "ExposureMetaUsageLimitType": ".types", "FeedbackCorrectionResult": ".types", - "FeedbackCorrectionResultRecommendation": ".types", - "FeedbackCorrectionResultRecommendationMedia": ".types", - "FeedbackCorrectionResultRecommendationMediaSourceChannel": ".types", "FeedbackCorrectionResultStatus": ".types", "FeedbackReadbackCorrection": ".types", "FeedbackReadbackCorrectionStatus": ".types", @@ -727,11 +432,8 @@ "HealthResponseVersion": ".types", "InternalServerError": ".errors", "ListMentionsRequestDetails": ".mentions", - "ListMentionsRequestEntityType": ".mentions", "ListMentionsRequestSentiment": ".mentions", - "ListRecommendationsRequestEntityType": ".recommendations", - "ListRecommendationsRequestMentionClass": ".recommendations", - "LookupEntitiesRequestType": ".entities", + "ListRecommendationsRequestClass": ".recommendations", "MeResponse": ".types", "MeResponseCredentialKind": ".types", "MeResponseUsage": ".types", @@ -747,7 +449,6 @@ "MentionCountsResponseSharedItem": ".types", "MentionCountsResponseSharedItemByChannelItem": ".types", "MentionListResponse": ".types", - "MentionListResponseEntity": ".types", "MentionListResponseUnlock": ".types", "MentionMedia": ".types", "MentionMediaSourceChannel": ".types", @@ -758,16 +459,22 @@ "MissedAlertChange": ".types", "MissingResultChange": ".types", "Monitor": ".types", + "MonitorAccess": ".types", + "MonitorAddEntitiesResponse": ".types", "MonitorAddTrackersResponse": ".types", "MonitorDeleteResponse": ".types", "MonitorEmailRecipientsItem": ".types", "MonitorEmailRecipientsItemInvitationStatus": ".types", + "MonitorEmailRecipientsItemRole": ".types", "MonitorEmailRecipientsItemStatus": ".types", + "MonitorEntityResult": ".types", + "MonitorEntityResultReason": ".types", "MonitorListResponse": ".types", "MonitorListResponseMonitorsItem": ".types", "MonitorListResponseMonitorsItemSlackIntegration": ".types", "MonitorMutationResponse": ".types", "MonitorMutationResponseMonitor": ".types", + "MonitorTeam": ".types", "MonitorTrackersResponse": ".types", "MonitorTrackersResponseTrackersItem": ".types", "NamedEntityRef": ".types", @@ -776,82 +483,13 @@ "OpenApiDocumentInfo": ".types", "OpenApiDocumentInfoContact": ".types", "OpenApiDocumentServersItem": ".types", - "OrganizationPageResponse": ".types", - "OrganizationPageResponseChannelsItem": ".types", - "OrganizationPageResponseChannelsItemSentiment": ".types", - "OrganizationPageResponseEntity": ".types", - "OrganizationPageResponseEntityOwnedChannelsItem": ".types", - "OrganizationPageResponseEntityOwnedProductsItem": ".types", - "OrganizationPageResponseEntityType": ".types", - "OrganizationPageResponseMentionsByMonthItem": ".types", - "OrganizationPageResponsePeopleItem": ".types", - "OrganizationPageResponsePeopleItemSentiment": ".types", - "OrganizationPageResponseProductsItem": ".types", - "OrganizationPageResponseProductsItemSentiment": ".types", - "OrganizationPageResponseRoleEdge": ".types", - "OrganizationPageResponseRoleEdgeLabel": ".types", - "OrganizationPageResponseRoleEdgePeopleItem": ".types", - "OrganizationPageResponseRoleEdgeRecentAppearancesItem": ".types", - "OrganizationPageResponseRoleEdgeRole": ".types", - "OrganizationPageResponseStats": ".types", - "OrganizationPageResponseTopicsItem": ".types", - "OrganizationPageResponseTopicsItemSentiment": ".types", "PaymentRequiredError": ".errors", - "PersonAppearanceListResponse": ".types", - "PersonAppearanceListResponseItemsItem": ".types", - "PersonAppearanceListResponseItemsItemPlatform": ".types", - "PersonAppearanceListResponseItemsItemSentiment": ".types", - "PersonAppearanceListResponseItemsItemType": ".types", - "PersonPageResponse": ".types", - "PersonPageResponseAppearancesByMonthItem": ".types", - "PersonPageResponseAppearancesItem": ".types", - "PersonPageResponseAppearancesItemPlatform": ".types", - "PersonPageResponseAppearancesItemSentiment": ".types", - "PersonPageResponseAppearancesItemType": ".types", - "PersonPageResponseBrandsItem": ".types", - "PersonPageResponseBrandsItemSentiment": ".types", - "PersonPageResponseEntity": ".types", - "PersonPageResponseEntityOwnedChannelsItem": ".types", - "PersonPageResponseEntityOwnedProductsItem": ".types", - "PersonPageResponseMentionsByMonthItem": ".types", - "PersonPageResponseMentionsItem": ".types", - "PersonPageResponseMentionsItemPlatform": ".types", - "PersonPageResponseMentionsItemSentiment": ".types", - "PersonPageResponseMentionsItemType": ".types", - "PersonPageResponsePeopleItem": ".types", - "PersonPageResponsePeopleItemRole": ".types", - "PersonPageResponsePeopleItemSentiment": ".types", - "PersonPageResponseProductsItem": ".types", - "PersonPageResponseProductsItemSentiment": ".types", - "PersonPageResponseRoleEdge": ".types", - "PersonPageResponseRoleEdgeLabel": ".types", - "PersonPageResponseRoleEdgeRole": ".types", - "PersonPageResponseStats": ".types", - "PersonPageResponseTopicsItem": ".types", - "PersonPageResponseTopicsItemSentiment": ".types", - "PreconditionFailedError": ".errors", - "ProductPageResponse": ".types", - "ProductPageResponseChannelsItem": ".types", - "ProductPageResponseChannelsItemSentiment": ".types", - "ProductPageResponseEntity": ".types", - "ProductPageResponseEntityOwner": ".types", - "ProductPageResponseEntityParentOrg": ".types", - "ProductPageResponseEntityType": ".types", - "ProductPageResponseMentionsByMonthItem": ".types", - "ProductPageResponseOpportunities": ".types", - "ProductPageResponseOrganizationsItem": ".types", - "ProductPageResponseOrganizationsItemSentiment": ".types", - "ProductPageResponsePeopleItem": ".types", - "ProductPageResponsePeopleItemSentiment": ".types", - "ProductPageResponseStats": ".types", - "ProductPageResponseTopicsItem": ".types", - "ProductPageResponseTopicsItemSentiment": ".types", - "PublishedExcerpt": ".types", - "PublishedExcerptPublicSourceClass": ".types", + "PublicationWindow": ".types", "Recommendation": ".types", + "RecommendationClass": ".types", "RecommendationEnrichmentItem": ".types", + "RecommendationEnrichmentItemClass": ".types", "RecommendationListResponse": ".types", - "RecommendationListResponseEntity": ".types", "RecommendationMedia": ".types", "RecommendationMediaSourceChannel": ".types", "ResolveCandidate": ".types", @@ -860,63 +498,28 @@ "ResolveSuggestion": ".types", "ResolveSuggestionMatch": ".types", "ResolveSuggestionReason": ".types", - "SearchEntitiesRequestType": ".entities", - "SearchResolveResponse": ".types", - "SearchResolveResponseEntity": ".types", "SearchTranscriptsRequestSource": ".transcripts", "ServiceUnavailableError": ".errors", "SignupSentResponse": ".types", "SignupSentResponseNext": ".types", "SignupSentResponseNextMethod": ".types", "SignupVerifiedResponse": ".types", - "SpeakerIdentificationSubmittedResponse": ".types", - "SpeakerIdentificationSubmittedResponseIdentification": ".types", - "SpeakerIdentificationSubmittedResponseIdentificationEntity": ".types", - "SpeakerIdentificationSubmittedResponseIdentificationStatus": ".types", + "SlackIntegrationListResponse": ".types", + "SlackIntegrationListResponseIntegrationsItem": ".types", + "SlackIntegrationListResponseIntegrationsItemChannelsItem": ".types", "StaleMetadataChange": ".types", - "SubmitCorrectionsRequestAnchor": ".corrections", - "SubmitCorrectionsRequestKind": ".corrections", + "SubmitFeedbackRequestCategory": ".feedback", "SubmitFeedbackRequestCorrectionsItem": ".feedback", + "SubmitFeedbackRequestCorrectionsItemClass": ".feedback", "SubmitFeedbackRequestCorrectionsItemIssueType": ".feedback", - "SubmitFeedbackRequestCorrectionsItemMentionClass": ".feedback", "SubmitFeedbackRequestCorrectionsItemReason": ".feedback", "SubmitFeedbackRequestCorrectionsItemSuggestedChange": ".feedback", "SubmitFeedbackRequestMethod": ".feedback", "SubmitFeedbackRequestType": ".feedback", - "TeamMember": ".types", - "TeamMemberRole": ".types", - "TeamMemberSeatType": ".types", - "TeamMemberSpend": ".types", - "TeamMemberSpendRole": ".types", - "TeamMemberSpendSeatType": ".types", - "TeamMembersResponse": ".types", - "TeamMembersResponseTeam": ".types", - "TeamSpendResponse": ".types", - "TeamUsageEvent": ".types", - "TeamUsageEventsResponse": ".types", "TooManyRequestsError": ".errors", - "TopicPageResponse": ".types", - "TopicPageResponseChannelsItem": ".types", - "TopicPageResponseChannelsItemSentiment": ".types", - "TopicPageResponseCompaniesItem": ".types", - "TopicPageResponseCompaniesItemSentiment": ".types", - "TopicPageResponseEntity": ".types", - "TopicPageResponseEntityType": ".types", - "TopicPageResponseMentionsByMonthItem": ".types", - "TopicPageResponseProductsItem": ".types", - "TopicPageResponseProductsItemSentiment": ".types", - "TopicPageResponseRelatedTopicsItem": ".types", - "TopicPageResponseRelatedTopicsItemSentiment": ".types", - "TopicPageResponseStats": ".types", - "TopicPageResponseVoicesItem": ".types", - "TopicPageResponseVoicesItemRole": ".types", - "TopicPageResponseVoicesItemSentiment": ".types", "Tracker": ".types", "TrackerListResponse": ".types", "TrackerMutationResponse": ".types", - "TranscriptEditSubmittedResponse": ".types", - "TranscriptEditSubmittedResponseEdit": ".types", - "TranscriptEditSubmittedResponseEditStatus": ".types", "TranscriptJob": ".types", "TranscriptJobCharge": ".types", "TranscriptJobChargeFrom": ".types", @@ -926,16 +529,6 @@ "TranscriptJobStatus": ".types", "TranscriptPending": ".types", "TranscriptPendingQuality": ".types", - "TranscriptPreparationRequired": ".types", - "TranscriptPreparationRequiredAction": ".types", - "TranscriptPreparationRequiredActionBody": ".types", - "TranscriptPreparationRequiredActionMethod": ".types", - "TranscriptPreparationRequiredLastAttempt": ".types", - "TranscriptPreparationRequiredQuality": ".types", - "TranscriptPreparationRequiredQuote": ".types", - "TranscriptPreparationRequiredQuoteCharge": ".types", - "TranscriptPreparationRequiredQuoteChargeFrom": ".types", - "TranscriptPreparationRequiredQuoteChargeUnit": ".types", "TranscriptPurchaseQuote": ".types", "TranscriptPurchaseQuoteBillingScope": ".types", "TranscriptPurchaseQuoteCharge": ".types", @@ -945,9 +538,13 @@ "TranscriptQuote": ".types", "TranscriptRequestListResponse": ".types", "TranscriptRequestListResponseRequestsItem": ".types", - "TranscriptRequestSubmitResponse": ".types", "TranscriptResponse": ".types", "TranscriptResponseAccess": ".types", + "TranscriptResponseAccessDetails": ".types", + "TranscriptResponseAccessDetailsQuote": ".types", + "TranscriptResponseAccessDetailsQuoteCharge": ".types", + "TranscriptResponseAccessDetailsQuoteChargeFrom": ".types", + "TranscriptResponseAccessDetailsQuoteChargeUnit": ".types", "TranscriptResponseAccessGate": ".types", "TranscriptResponseAccessReason": ".types", "TranscriptResponseAccessType": ".types", @@ -961,19 +558,25 @@ "TranscriptResponseSpeakersItem": ".types", "TranscriptResult": ".types", "TranscriptResult_Pending": ".types", - "TranscriptResult_PreparationRequired": ".types", "TranscriptResult_Ready": ".types", "TranscriptSearchChunk": ".types", "TranscriptSearchResponse": ".types", "TranscriptSearchResponseAccess": ".types", + "TranscriptSearchResponseAccessDetails": ".types", + "TranscriptSearchResponseAccessDetailsQuote": ".types", + "TranscriptSearchResponseAccessDetailsQuoteCharge": ".types", + "TranscriptSearchResponseAccessDetailsQuoteChargeFrom": ".types", + "TranscriptSearchResponseAccessDetailsQuoteChargeUnit": ".types", "TranscriptSearchResponseAccessGate": ".types", "TranscriptSearchResponseAccessReason": ".types", "TranscriptSearchResponseAccessType": ".types", "TranscriptSearchResponseAccessUnlock": ".types", "TranscriptSearchResponseAccessUnlockAction": ".types", "TranscriptSearchResponseFilters": ".types", + "TranscriptSearchResponseFiltersKindItem": ".types", "TranscriptSearchResponseSearchIndex": ".types", "TranscriptSearchResponseSearchIndexState": ".types", + "TranscriptSearchResponseUnlock": ".types", "TranscriptSettings": ".types", "TranscriptSettingsQuality": ".types", "TranscriptVideo": ".types", @@ -982,34 +585,21 @@ "UpdateSettingsMeRequestTranscripts": ".me", "UpdateSettingsMeRequestTranscriptsQuality": ".me", "UpdateTrackersRequestPersonMatchMode": ".trackers", - "VideoCaptionsResponse": ".types", - "VideoMergeListResponse": ".types", - "VideoMergeListResponseMergesItem": ".types", - "VideoMergeListResponseMergesItemStatus": ".types", - "VideoMergeSubmittedResponse": ".types", - "VideoMergeSubmittedResponseMerge": ".types", - "VideoMergeSubmittedResponseMergeStatus": ".types", "WebhookSecretRotateResponse": ".types", - "WithdrawnResponse": ".types", "WrongClassificationChange": ".types", - "WrongClassificationChangeMentionClass": ".types", + "WrongClassificationChangeClass": ".types", "WrongEntityChange": ".types", "WrongEntityTypeChange": ".types", "WrongEntityTypeChangeField": ".types", "channels": ".channels", - "corrections": ".corrections", "entities": ".entities", "feedback": ".feedback", "health": ".health", + "integrations": ".integrations", "me": ".me", "mentions": ".mentions", "monitors": ".monitors", - "organizations": ".organizations", - "people": ".people", - "products": ".products", "recommendations": ".recommendations", - "team": ".team", - "topics": ".topics", "trackers": ".trackers", "transcripts": ".transcripts", } @@ -1052,39 +642,15 @@ def __dir__(): "ChannelCoverageResponse", "ChannelCoverageResponseChannel", "ChannelCoverageResponseChannelSourceMix", - "ChannelGuestListResponse", - "ChannelGuestListResponseExportCapabilities", - "ChannelGuestListResponseItemsItem", - "ChannelGuestListResponseItemsItemSentiment", - "ChannelPageResponse", - "ChannelPageResponseChannelInfo", - "ChannelPageResponseEntity", - "ChannelPageResponseEntityOwner", - "ChannelPageResponseEntityType", - "ChannelPageResponseEpisodesByMonthItem", - "ChannelPageResponseEpisodesItem", - "ChannelPageResponseEpisodesItemPlatform", - "ChannelPageResponseEpisodesItemSentiment", - "ChannelPageResponseEpisodesItemTimestamp", - "ChannelPageResponseEpisodesItemType", - "ChannelPageResponseGuestsItem", - "ChannelPageResponseGuestsItemRole", - "ChannelPageResponseGuestsItemSentiment", - "ChannelPageResponseHostsDetailedItem", - "ChannelPageResponseHostsDetailedItemSentiment", - "ChannelPageResponseOrganizationsItem", - "ChannelPageResponseOrganizationsItemSentiment", - "ChannelPageResponseProductsItem", - "ChannelPageResponseProductsItemSentiment", - "ChannelPageResponseRecommendationsSummary", - "ChannelPageResponseStats", - "ChannelPageResponseTopicsItem", - "ChannelPageResponseTopicsItemSentiment", "ChannelSponsor", - "ChannelSponsorEntity", "ChannelSponsorSponsorStatus", "ChannelSponsorsResponse", "ChannelSponsorsResponseAccess", + "ChannelSponsorsResponseAccessDetails", + "ChannelSponsorsResponseAccessDetailsQuote", + "ChannelSponsorsResponseAccessDetailsQuoteCharge", + "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom", + "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit", "ChannelSponsorsResponseAccessGate", "ChannelSponsorsResponseAccessReason", "ChannelSponsorsResponseAccessType", @@ -1096,8 +662,6 @@ def __dir__(): "ChannelVideosResponseChannel", "ChannelVideosResponseEpisodesItem", "ConflictError", - "CorrectionAcceptedResponse", - "CorrectionAcceptedResponseKind", "CountMentionsRequestMode", "CreateMonitorsRequestNotifyFrequency", "CreateTrackersRequestEntityType", @@ -1107,16 +671,15 @@ def __dir__(): "DeliveryIssueChange", "DeliveryIssueChangeChannel", "Entity", - "EntityCard", - "EntityCardsResponse", - "EntityChannelListResponse", - "EntityChannelListResponseExportCapabilities", - "EntityChannelListResponseItemsItem", "EntityDetailRecommendationsSummary", "EntityDetailResponse", - "EntityLookupResponse", "EntityMomentumResponse", "EntityMomentumResponseAccess", + "EntityMomentumResponseAccessDetails", + "EntityMomentumResponseAccessDetailsQuote", + "EntityMomentumResponseAccessDetailsQuoteCharge", + "EntityMomentumResponseAccessDetailsQuoteChargeFrom", + "EntityMomentumResponseAccessDetailsQuoteChargeUnit", "EntityMomentumResponseAccessGate", "EntityMomentumResponseAccessReason", "EntityMomentumResponseAccessType", @@ -1126,48 +689,23 @@ def __dir__(): "EntityMomentumResponseTopShowsItem", "EntityMomentumResponseVerdict", "EntityMomentumResponseVolume", - "EntityOrganizationListResponse", - "EntityOrganizationListResponseExportCapabilities", - "EntityOrganizationListResponseItemsItem", - "EntityOrganizationListResponseItemsItemSentiment", - "EntityPageMention", - "EntityPageMentionExcerpt", - "EntityPageMentionExcerptPublicSourceClass", - "EntityPageMentionPlatform", - "EntityPageMentionSentiment", - "EntityPageMentionType", - "EntityPeopleListResponse", - "EntityPeopleListResponseExportCapabilities", - "EntityPeopleListResponseItemsItem", - "EntityPeopleListResponseItemsItemSentiment", - "EntityPeopleListResponsePeopleMode", - "EntityProductListResponse", - "EntityProductListResponseExportCapabilities", - "EntityProductListResponseItemsItem", - "EntityProductListResponseItemsItemSentiment", "EntityRef", "EntityResolveResponse", "EntityResolveResponseAsk", "EntityResolveResponseAskOptionsItem", "EntityResolveResponseConfidence", - "EntitySearchResponse", - "EntitySearchResult", - "EntitySearchResultRecommendationsSummary", - "EntityTopicListResponse", - "EntityTopicListResponseExportCapabilities", - "EntityTopicListResponseItemsItem", - "EntityTopicListResponseItemsItemSentiment", "Error", "ErrorError", + "ErrorErrorDetails", + "ErrorErrorDetailsQuote", + "ErrorErrorDetailsQuoteCharge", + "ErrorErrorDetailsQuoteChargeFrom", + "ErrorErrorDetailsQuoteChargeUnit", "ErrorErrorGate", "ErrorErrorReason", "ErrorErrorType", "ErrorErrorUnlock", "ErrorErrorUnlockAction", - "ErrorQuote", - "ErrorQuoteCharge", - "ErrorQuoteChargeFrom", - "ErrorQuoteChargeUnit", "ErrorResource", "ErrorResourceChart", "ErrorResourceCommercial", @@ -1200,45 +738,7 @@ def __dir__(): "ErrorResource_Requests", "ErrorResource_Rows", "ErrorResource_SidebarRows", - "ExposureMeta", - "ExposureMetaAccess", - "ExposureMetaAccessChart", - "ExposureMetaAccessCls", - "ExposureMetaAccessFreshness", - "ExposureMetaAccessLadder", - "ExposureMetaAccessRows", - "ExposureMetaAccessRowsEntities", - "ExposureMetaAccessRowsMedia", - "ExposureMetaAccessRowsTopics", - "ExposureMetaAccessUnlock", - "ExposureMetaAccessUnlockLimitAction", - "ExposureMetaAccessUnlockSrc", - "ExposureMetaAccessView", - "ExposureMetaAccessWithheldItem", - "ExposureMetaAccessWithheldItemKind", - "ExposureMetaAccessWithheldItemParam", - "ExposureMetaAccessWithheldItemSection", - "ExposureMetaAccessWithheldItemWhat", - "ExposureMetaCredits", - "ExposureMetaCreditsOnDemand", - "ExposureMetaCreditsPlan", - "ExposureMetaFreeLimit", - "ExposureMetaLimitAction", - "ExposureMetaLimits", - "ExposureMetaRecentPreview", - "ExposureMetaRecentPreviewExperiment", - "ExposureMetaRecentPreviewMentions", - "ExposureMetaRecentPreviewMentionsExperiment", - "ExposureMetaRecentPreviewMentionsSubject", - "ExposureMetaRecentPreviewMentionsTeaserItemsItem", - "ExposureMetaRecentPreviewSubject", - "ExposureMetaRecentPreviewTeaserItemsItem", - "ExposureMetaTotals", - "ExposureMetaUsageLimitType", "FeedbackCorrectionResult", - "FeedbackCorrectionResultRecommendation", - "FeedbackCorrectionResultRecommendationMedia", - "FeedbackCorrectionResultRecommendationMediaSourceChannel", "FeedbackCorrectionResultStatus", "FeedbackReadbackCorrection", "FeedbackReadbackCorrectionStatus", @@ -1253,11 +753,8 @@ def __dir__(): "HealthResponseVersion", "InternalServerError", "ListMentionsRequestDetails", - "ListMentionsRequestEntityType", "ListMentionsRequestSentiment", - "ListRecommendationsRequestEntityType", - "ListRecommendationsRequestMentionClass", - "LookupEntitiesRequestType", + "ListRecommendationsRequestClass", "MeResponse", "MeResponseCredentialKind", "MeResponseUsage", @@ -1273,7 +770,6 @@ def __dir__(): "MentionCountsResponseSharedItem", "MentionCountsResponseSharedItemByChannelItem", "MentionListResponse", - "MentionListResponseEntity", "MentionListResponseUnlock", "MentionMedia", "MentionMediaSourceChannel", @@ -1284,16 +780,22 @@ def __dir__(): "MissedAlertChange", "MissingResultChange", "Monitor", + "MonitorAccess", + "MonitorAddEntitiesResponse", "MonitorAddTrackersResponse", "MonitorDeleteResponse", "MonitorEmailRecipientsItem", "MonitorEmailRecipientsItemInvitationStatus", + "MonitorEmailRecipientsItemRole", "MonitorEmailRecipientsItemStatus", + "MonitorEntityResult", + "MonitorEntityResultReason", "MonitorListResponse", "MonitorListResponseMonitorsItem", "MonitorListResponseMonitorsItemSlackIntegration", "MonitorMutationResponse", "MonitorMutationResponseMonitor", + "MonitorTeam", "MonitorTrackersResponse", "MonitorTrackersResponseTrackersItem", "NamedEntityRef", @@ -1302,82 +804,13 @@ def __dir__(): "OpenApiDocumentInfo", "OpenApiDocumentInfoContact", "OpenApiDocumentServersItem", - "OrganizationPageResponse", - "OrganizationPageResponseChannelsItem", - "OrganizationPageResponseChannelsItemSentiment", - "OrganizationPageResponseEntity", - "OrganizationPageResponseEntityOwnedChannelsItem", - "OrganizationPageResponseEntityOwnedProductsItem", - "OrganizationPageResponseEntityType", - "OrganizationPageResponseMentionsByMonthItem", - "OrganizationPageResponsePeopleItem", - "OrganizationPageResponsePeopleItemSentiment", - "OrganizationPageResponseProductsItem", - "OrganizationPageResponseProductsItemSentiment", - "OrganizationPageResponseRoleEdge", - "OrganizationPageResponseRoleEdgeLabel", - "OrganizationPageResponseRoleEdgePeopleItem", - "OrganizationPageResponseRoleEdgeRecentAppearancesItem", - "OrganizationPageResponseRoleEdgeRole", - "OrganizationPageResponseStats", - "OrganizationPageResponseTopicsItem", - "OrganizationPageResponseTopicsItemSentiment", "PaymentRequiredError", - "PersonAppearanceListResponse", - "PersonAppearanceListResponseItemsItem", - "PersonAppearanceListResponseItemsItemPlatform", - "PersonAppearanceListResponseItemsItemSentiment", - "PersonAppearanceListResponseItemsItemType", - "PersonPageResponse", - "PersonPageResponseAppearancesByMonthItem", - "PersonPageResponseAppearancesItem", - "PersonPageResponseAppearancesItemPlatform", - "PersonPageResponseAppearancesItemSentiment", - "PersonPageResponseAppearancesItemType", - "PersonPageResponseBrandsItem", - "PersonPageResponseBrandsItemSentiment", - "PersonPageResponseEntity", - "PersonPageResponseEntityOwnedChannelsItem", - "PersonPageResponseEntityOwnedProductsItem", - "PersonPageResponseMentionsByMonthItem", - "PersonPageResponseMentionsItem", - "PersonPageResponseMentionsItemPlatform", - "PersonPageResponseMentionsItemSentiment", - "PersonPageResponseMentionsItemType", - "PersonPageResponsePeopleItem", - "PersonPageResponsePeopleItemRole", - "PersonPageResponsePeopleItemSentiment", - "PersonPageResponseProductsItem", - "PersonPageResponseProductsItemSentiment", - "PersonPageResponseRoleEdge", - "PersonPageResponseRoleEdgeLabel", - "PersonPageResponseRoleEdgeRole", - "PersonPageResponseStats", - "PersonPageResponseTopicsItem", - "PersonPageResponseTopicsItemSentiment", - "PreconditionFailedError", - "ProductPageResponse", - "ProductPageResponseChannelsItem", - "ProductPageResponseChannelsItemSentiment", - "ProductPageResponseEntity", - "ProductPageResponseEntityOwner", - "ProductPageResponseEntityParentOrg", - "ProductPageResponseEntityType", - "ProductPageResponseMentionsByMonthItem", - "ProductPageResponseOpportunities", - "ProductPageResponseOrganizationsItem", - "ProductPageResponseOrganizationsItemSentiment", - "ProductPageResponsePeopleItem", - "ProductPageResponsePeopleItemSentiment", - "ProductPageResponseStats", - "ProductPageResponseTopicsItem", - "ProductPageResponseTopicsItemSentiment", - "PublishedExcerpt", - "PublishedExcerptPublicSourceClass", + "PublicationWindow", "Recommendation", + "RecommendationClass", "RecommendationEnrichmentItem", + "RecommendationEnrichmentItemClass", "RecommendationListResponse", - "RecommendationListResponseEntity", "RecommendationMedia", "RecommendationMediaSourceChannel", "ResolveCandidate", @@ -1386,63 +819,28 @@ def __dir__(): "ResolveSuggestion", "ResolveSuggestionMatch", "ResolveSuggestionReason", - "SearchEntitiesRequestType", - "SearchResolveResponse", - "SearchResolveResponseEntity", "SearchTranscriptsRequestSource", "ServiceUnavailableError", "SignupSentResponse", "SignupSentResponseNext", "SignupSentResponseNextMethod", "SignupVerifiedResponse", - "SpeakerIdentificationSubmittedResponse", - "SpeakerIdentificationSubmittedResponseIdentification", - "SpeakerIdentificationSubmittedResponseIdentificationEntity", - "SpeakerIdentificationSubmittedResponseIdentificationStatus", + "SlackIntegrationListResponse", + "SlackIntegrationListResponseIntegrationsItem", + "SlackIntegrationListResponseIntegrationsItemChannelsItem", "StaleMetadataChange", - "SubmitCorrectionsRequestAnchor", - "SubmitCorrectionsRequestKind", + "SubmitFeedbackRequestCategory", "SubmitFeedbackRequestCorrectionsItem", + "SubmitFeedbackRequestCorrectionsItemClass", "SubmitFeedbackRequestCorrectionsItemIssueType", - "SubmitFeedbackRequestCorrectionsItemMentionClass", "SubmitFeedbackRequestCorrectionsItemReason", "SubmitFeedbackRequestCorrectionsItemSuggestedChange", "SubmitFeedbackRequestMethod", "SubmitFeedbackRequestType", - "TeamMember", - "TeamMemberRole", - "TeamMemberSeatType", - "TeamMemberSpend", - "TeamMemberSpendRole", - "TeamMemberSpendSeatType", - "TeamMembersResponse", - "TeamMembersResponseTeam", - "TeamSpendResponse", - "TeamUsageEvent", - "TeamUsageEventsResponse", "TooManyRequestsError", - "TopicPageResponse", - "TopicPageResponseChannelsItem", - "TopicPageResponseChannelsItemSentiment", - "TopicPageResponseCompaniesItem", - "TopicPageResponseCompaniesItemSentiment", - "TopicPageResponseEntity", - "TopicPageResponseEntityType", - "TopicPageResponseMentionsByMonthItem", - "TopicPageResponseProductsItem", - "TopicPageResponseProductsItemSentiment", - "TopicPageResponseRelatedTopicsItem", - "TopicPageResponseRelatedTopicsItemSentiment", - "TopicPageResponseStats", - "TopicPageResponseVoicesItem", - "TopicPageResponseVoicesItemRole", - "TopicPageResponseVoicesItemSentiment", "Tracker", "TrackerListResponse", "TrackerMutationResponse", - "TranscriptEditSubmittedResponse", - "TranscriptEditSubmittedResponseEdit", - "TranscriptEditSubmittedResponseEditStatus", "TranscriptJob", "TranscriptJobCharge", "TranscriptJobChargeFrom", @@ -1452,16 +850,6 @@ def __dir__(): "TranscriptJobStatus", "TranscriptPending", "TranscriptPendingQuality", - "TranscriptPreparationRequired", - "TranscriptPreparationRequiredAction", - "TranscriptPreparationRequiredActionBody", - "TranscriptPreparationRequiredActionMethod", - "TranscriptPreparationRequiredLastAttempt", - "TranscriptPreparationRequiredQuality", - "TranscriptPreparationRequiredQuote", - "TranscriptPreparationRequiredQuoteCharge", - "TranscriptPreparationRequiredQuoteChargeFrom", - "TranscriptPreparationRequiredQuoteChargeUnit", "TranscriptPurchaseQuote", "TranscriptPurchaseQuoteBillingScope", "TranscriptPurchaseQuoteCharge", @@ -1471,9 +859,13 @@ def __dir__(): "TranscriptQuote", "TranscriptRequestListResponse", "TranscriptRequestListResponseRequestsItem", - "TranscriptRequestSubmitResponse", "TranscriptResponse", "TranscriptResponseAccess", + "TranscriptResponseAccessDetails", + "TranscriptResponseAccessDetailsQuote", + "TranscriptResponseAccessDetailsQuoteCharge", + "TranscriptResponseAccessDetailsQuoteChargeFrom", + "TranscriptResponseAccessDetailsQuoteChargeUnit", "TranscriptResponseAccessGate", "TranscriptResponseAccessReason", "TranscriptResponseAccessType", @@ -1487,19 +879,25 @@ def __dir__(): "TranscriptResponseSpeakersItem", "TranscriptResult", "TranscriptResult_Pending", - "TranscriptResult_PreparationRequired", "TranscriptResult_Ready", "TranscriptSearchChunk", "TranscriptSearchResponse", "TranscriptSearchResponseAccess", + "TranscriptSearchResponseAccessDetails", + "TranscriptSearchResponseAccessDetailsQuote", + "TranscriptSearchResponseAccessDetailsQuoteCharge", + "TranscriptSearchResponseAccessDetailsQuoteChargeFrom", + "TranscriptSearchResponseAccessDetailsQuoteChargeUnit", "TranscriptSearchResponseAccessGate", "TranscriptSearchResponseAccessReason", "TranscriptSearchResponseAccessType", "TranscriptSearchResponseAccessUnlock", "TranscriptSearchResponseAccessUnlockAction", "TranscriptSearchResponseFilters", + "TranscriptSearchResponseFiltersKindItem", "TranscriptSearchResponseSearchIndex", "TranscriptSearchResponseSearchIndexState", + "TranscriptSearchResponseUnlock", "TranscriptSettings", "TranscriptSettingsQuality", "TranscriptVideo", @@ -1508,37 +906,23 @@ def __dir__(): "UpdateSettingsMeRequestTranscripts", "UpdateSettingsMeRequestTranscriptsQuality", "UpdateTrackersRequestPersonMatchMode", - "VideoCaptionsResponse", - "VideoMergeListResponse", - "VideoMergeListResponseMergesItem", - "VideoMergeListResponseMergesItemStatus", - "VideoMergeSubmittedResponse", - "VideoMergeSubmittedResponseMerge", - "VideoMergeSubmittedResponseMergeStatus", "WebhookSecretRotateResponse", - "WithdrawnResponse", "WrongClassificationChange", - "WrongClassificationChangeMentionClass", + "WrongClassificationChangeClass", "WrongEntityChange", "WrongEntityTypeChange", "WrongEntityTypeChangeField", "channels", - "corrections", "entities", "feedback", "health", + "integrations", "me", "mentions", "monitors", - "organizations", - "people", - "products", "recommendations", - "team", - "topics", "trackers", "transcripts", ] from ._package import __version__, homepage, docs, api_base, openapi, llms_txt, docs_llms_txt -from .transcripts.prepare import PreparationError, PreparationFailedError, PreparationTimeoutError, PremiumUnavailableError diff --git a/src/arcmira/_package.py b/src/arcmira/_package.py index a2edf53..7e829cd 100644 --- a/src/arcmira/_package.py +++ b/src/arcmira/_package.py @@ -1,5 +1,5 @@ # Written by scripts/install-generated.py from VERSION. -__version__ = '0.3.0' +__version__ = '0.4.0' homepage = "https://arcmira.com" docs = "https://arcmira.com/docs" api_base = "https://api.arcmira.com/v1" diff --git a/src/arcmira/channels/__init__.py b/src/arcmira/channels/__init__.py index c84a158..88421ee 100644 --- a/src/arcmira/channels/__init__.py +++ b/src/arcmira/channels/__init__.py @@ -6,48 +6,10 @@ from importlib import import_module if typing.TYPE_CHECKING: - from . import guests, related, sponsors, videos - from .guests import ListGuestsRequestIsAppearance, ListGuestsRequestMode, ListGuestsRequestOrder - from .related import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) + from . import sponsors, videos from .sponsors import ListSponsorsRequestStatus _dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".related", - "ChannelsRelatedRequestMode": ".related", - "ChannelsRelatedRequestOrder": ".related", - "ListGuestsRequestIsAppearance": ".guests", - "ListGuestsRequestMode": ".guests", - "ListGuestsRequestOrder": ".guests", "ListSponsorsRequestStatus": ".sponsors", - "OrganizationsRelatedRequestIsAppearance": ".related", - "OrganizationsRelatedRequestMode": ".related", - "OrganizationsRelatedRequestOrder": ".related", - "PeopleRelatedRequestIsAppearance": ".related", - "PeopleRelatedRequestMode": ".related", - "PeopleRelatedRequestOrder": ".related", - "ProductsRelatedRequestIsAppearance": ".related", - "ProductsRelatedRequestMode": ".related", - "ProductsRelatedRequestOrder": ".related", - "TopicsRelatedRequestIsAppearance": ".related", - "TopicsRelatedRequestMode": ".related", - "TopicsRelatedRequestOrder": ".related", - "guests": ".guests", - "related": ".related", "sponsors": ".sponsors", "videos": ".videos", } @@ -74,28 +36,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "ListGuestsRequestIsAppearance", - "ListGuestsRequestMode", - "ListGuestsRequestOrder", - "ListSponsorsRequestStatus", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", - "guests", - "related", - "sponsors", - "videos", -] +__all__ = ["ListSponsorsRequestStatus", "sponsors", "videos"] diff --git a/src/arcmira/channels/client.py b/src/arcmira/channels/client.py index 29c6d92..9f24572 100644 --- a/src/arcmira/channels/client.py +++ b/src/arcmira/channels/client.py @@ -7,12 +7,9 @@ from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions from ..types.channel_coverage_response import ChannelCoverageResponse -from ..types.channel_page_response import ChannelPageResponse from .raw_client import AsyncRawChannelsClient, RawChannelsClient if typing.TYPE_CHECKING: - from .guests.client import AsyncGuestsClient, GuestsClient - from .related.client import AsyncRelatedClient, RelatedClient from .sponsors.client import AsyncSponsorsClient, SponsorsClient from .videos.client import AsyncVideosClient, VideosClient @@ -23,8 +20,6 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper self._sponsors: typing.Optional[SponsorsClient] = None self._videos: typing.Optional[VideosClient] = None - self._related: typing.Optional[RelatedClient] = None - self._guests: typing.Optional[GuestsClient] = None @property def with_raw_response(self) -> RawChannelsClient: @@ -70,37 +65,6 @@ def coverage( _response = self._raw_client.coverage(channel_id, request_options=request_options) return _response.data - def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> ChannelPageResponse: - """ - Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ChannelPageResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.channels.get( - slug="slug", - ) - """ - _response = self._raw_client.get(slug, request_options=request_options) - return _response.data - @property def sponsors(self): if self._sponsors is None: @@ -117,22 +81,6 @@ def videos(self): self._videos = VideosClient(client_wrapper=self._client_wrapper) return self._videos - @property - def related(self): - if self._related is None: - from .related.client import RelatedClient # noqa: E402 - - self._related = RelatedClient(client_wrapper=self._client_wrapper) - return self._related - - @property - def guests(self): - if self._guests is None: - from .guests.client import GuestsClient # noqa: E402 - - self._guests = GuestsClient(client_wrapper=self._client_wrapper) - return self._guests - class AsyncChannelsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -140,8 +88,6 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper self._sponsors: typing.Optional[AsyncSponsorsClient] = None self._videos: typing.Optional[AsyncVideosClient] = None - self._related: typing.Optional[AsyncRelatedClient] = None - self._guests: typing.Optional[AsyncGuestsClient] = None @property def with_raw_response(self) -> AsyncRawChannelsClient: @@ -195,45 +141,6 @@ async def main() -> None: _response = await self._raw_client.coverage(channel_id, request_options=request_options) return _response.data - async def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> ChannelPageResponse: - """ - Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ChannelPageResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.channels.get( - slug="slug", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(slug, request_options=request_options) - return _response.data - @property def sponsors(self): if self._sponsors is None: @@ -249,19 +156,3 @@ def videos(self): self._videos = AsyncVideosClient(client_wrapper=self._client_wrapper) return self._videos - - @property - def related(self): - if self._related is None: - from .related.client import AsyncRelatedClient # noqa: E402 - - self._related = AsyncRelatedClient(client_wrapper=self._client_wrapper) - return self._related - - @property - def guests(self): - if self._guests is None: - from .guests.client import AsyncGuestsClient # noqa: E402 - - self._guests = AsyncGuestsClient(client_wrapper=self._client_wrapper) - return self._guests diff --git a/src/arcmira/channels/guests/__init__.py b/src/arcmira/channels/guests/__init__.py deleted file mode 100644 index a406741..0000000 --- a/src/arcmira/channels/guests/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ListGuestsRequestIsAppearance, ListGuestsRequestMode, ListGuestsRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ListGuestsRequestIsAppearance": ".types", - "ListGuestsRequestMode": ".types", - "ListGuestsRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListGuestsRequestIsAppearance", "ListGuestsRequestMode", "ListGuestsRequestOrder"] diff --git a/src/arcmira/channels/guests/client.py b/src/arcmira/channels/guests/client.py deleted file mode 100644 index 0fe3e45..0000000 --- a/src/arcmira/channels/guests/client.py +++ /dev/null @@ -1,218 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.channel_guest_list_response import ChannelGuestListResponse -from ...types.channel_guest_list_response_items_item import ChannelGuestListResponseItemsItem -from .raw_client import AsyncRawGuestsClient, RawGuestsClient -from .types.list_guests_request_is_appearance import ListGuestsRequestIsAppearance -from .types.list_guests_request_mode import ListGuestsRequestMode -from .types.list_guests_request_order import ListGuestsRequestOrder - - -class GuestsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawGuestsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawGuestsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawGuestsClient - """ - return self._raw_client - - def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListGuestsRequestOrder] = None, - mode: typing.Optional[ListGuestsRequestMode] = None, - is_appearance: typing.Optional[ListGuestsRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse]: - """ - People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListGuestsRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListGuestsRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListGuestsRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.guests.list( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.list( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncGuestsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawGuestsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawGuestsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawGuestsClient - """ - return self._raw_client - - async def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListGuestsRequestOrder] = None, - mode: typing.Optional[ListGuestsRequestMode] = None, - is_appearance: typing.Optional[ListGuestsRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse]: - """ - People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListGuestsRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListGuestsRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListGuestsRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.guests.list( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.list( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/channels/guests/raw_client.py b/src/arcmira/channels/guests/raw_client.py deleted file mode 100644 index 15a5ad9..0000000 --- a/src/arcmira/channels/guests/raw_client.py +++ /dev/null @@ -1,397 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.channel_guest_list_response import ChannelGuestListResponse -from ...types.channel_guest_list_response_items_item import ChannelGuestListResponseItemsItem -from ...types.error import Error -from .types.list_guests_request_is_appearance import ListGuestsRequestIsAppearance -from .types.list_guests_request_mode import ListGuestsRequestMode -from .types.list_guests_request_order import ListGuestsRequestOrder -from pydantic import ValidationError - - -class RawGuestsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListGuestsRequestOrder] = None, - mode: typing.Optional[ListGuestsRequestMode] = None, - is_appearance: typing.Optional[ListGuestsRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse]: - """ - People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListGuestsRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListGuestsRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListGuestsRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/guests", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - ChannelGuestListResponse, - parse_obj_as( - type_=ChannelGuestListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawGuestsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListGuestsRequestOrder] = None, - mode: typing.Optional[ListGuestsRequestMode] = None, - is_appearance: typing.Optional[ListGuestsRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse]: - """ - People who appeared as guests on the channel, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListGuestsRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListGuestsRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListGuestsRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[ChannelGuestListResponseItemsItem, ChannelGuestListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/guests", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - ChannelGuestListResponse, - parse_obj_as( - type_=ChannelGuestListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.list( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/channels/guests/types/__init__.py b/src/arcmira/channels/guests/types/__init__.py deleted file mode 100644 index f6546f5..0000000 --- a/src/arcmira/channels/guests/types/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .list_guests_request_is_appearance import ListGuestsRequestIsAppearance - from .list_guests_request_mode import ListGuestsRequestMode - from .list_guests_request_order import ListGuestsRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ListGuestsRequestIsAppearance": ".list_guests_request_is_appearance", - "ListGuestsRequestMode": ".list_guests_request_mode", - "ListGuestsRequestOrder": ".list_guests_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListGuestsRequestIsAppearance", "ListGuestsRequestMode", "ListGuestsRequestOrder"] diff --git a/src/arcmira/channels/guests/types/list_guests_request_is_appearance.py b/src/arcmira/channels/guests/types/list_guests_request_is_appearance.py deleted file mode 100644 index 472baf1..0000000 --- a/src/arcmira/channels/guests/types/list_guests_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListGuestsRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/guests/types/list_guests_request_mode.py b/src/arcmira/channels/guests/types/list_guests_request_mode.py deleted file mode 100644 index f4e8b9d..0000000 --- a/src/arcmira/channels/guests/types/list_guests_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListGuestsRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/guests/types/list_guests_request_order.py b/src/arcmira/channels/guests/types/list_guests_request_order.py deleted file mode 100644 index acfff67..0000000 --- a/src/arcmira/channels/guests/types/list_guests_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListGuestsRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/raw_client.py b/src/arcmira/channels/raw_client.py index a81e914..9c7ae90 100644 --- a/src/arcmira/channels/raw_client.py +++ b/src/arcmira/channels/raw_client.py @@ -14,11 +14,9 @@ from ..errors.forbidden_error import ForbiddenError from ..errors.internal_server_error import InternalServerError from ..errors.not_found_error import NotFoundError -from ..errors.payment_required_error import PaymentRequiredError from ..errors.too_many_requests_error import TooManyRequestsError from ..errors.unauthorized_error import UnauthorizedError from ..types.channel_coverage_response import ChannelCoverageResponse -from ..types.channel_page_response import ChannelPageResponse from ..types.error import Error from pydantic import ValidationError @@ -136,126 +134,6 @@ def coverage( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ChannelPageResponse]: - """ - Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[ChannelPageResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ChannelPageResponse, - parse_obj_as( - type_=ChannelPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - class AsyncRawChannelsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -369,123 +247,3 @@ async def coverage( status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ChannelPageResponse]: - """ - Channel pages include a recommendations_summary teaser: sponsor_count for all callers; top_sponsors additionally requires a Pro+ plan. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[ChannelPageResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ChannelPageResponse, - parse_obj_as( - type_=ChannelPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/channels/related/__init__.py b/src/arcmira/channels/related/__init__.py deleted file mode 100644 index bed85d9..0000000 --- a/src/arcmira/channels/related/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".types", - "ChannelsRelatedRequestMode": ".types", - "ChannelsRelatedRequestOrder": ".types", - "OrganizationsRelatedRequestIsAppearance": ".types", - "OrganizationsRelatedRequestMode": ".types", - "OrganizationsRelatedRequestOrder": ".types", - "PeopleRelatedRequestIsAppearance": ".types", - "PeopleRelatedRequestMode": ".types", - "PeopleRelatedRequestOrder": ".types", - "ProductsRelatedRequestIsAppearance": ".types", - "ProductsRelatedRequestMode": ".types", - "ProductsRelatedRequestOrder": ".types", - "TopicsRelatedRequestIsAppearance": ".types", - "TopicsRelatedRequestMode": ".types", - "TopicsRelatedRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/channels/related/client.py b/src/arcmira/channels/related/client.py deleted file mode 100644 index 7d19985..0000000 --- a/src/arcmira/channels/related/client.py +++ /dev/null @@ -1,930 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .raw_client import AsyncRawRelatedClient, RawRelatedClient -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder - - -class RelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRelatedClient - """ - return self._raw_client - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.related.topics( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.related.people( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.related.organizations( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.related.products( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.channels.related.channels( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRelatedClient - """ - return self._raw_client - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.related.topics( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.related.people( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.related.organizations( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.related.products( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.channels.related.channels( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/channels/related/raw_client.py b/src/arcmira/channels/related/raw_client.py deleted file mode 100644 index 8f405ab..0000000 --- a/src/arcmira/channels/related/raw_client.py +++ /dev/null @@ -1,1861 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from ...types.error import Error -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder -from pydantic import ValidationError - - -class RawRelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this channel in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/channels/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/channels/related/types/__init__.py b/src/arcmira/channels/related/types/__init__.py deleted file mode 100644 index 1f3a8a7..0000000 --- a/src/arcmira/channels/related/types/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance - from .channels_related_request_mode import ChannelsRelatedRequestMode - from .channels_related_request_order import ChannelsRelatedRequestOrder - from .organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance - from .organizations_related_request_mode import OrganizationsRelatedRequestMode - from .organizations_related_request_order import OrganizationsRelatedRequestOrder - from .people_related_request_is_appearance import PeopleRelatedRequestIsAppearance - from .people_related_request_mode import PeopleRelatedRequestMode - from .people_related_request_order import PeopleRelatedRequestOrder - from .products_related_request_is_appearance import ProductsRelatedRequestIsAppearance - from .products_related_request_mode import ProductsRelatedRequestMode - from .products_related_request_order import ProductsRelatedRequestOrder - from .topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance - from .topics_related_request_mode import TopicsRelatedRequestMode - from .topics_related_request_order import TopicsRelatedRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".channels_related_request_is_appearance", - "ChannelsRelatedRequestMode": ".channels_related_request_mode", - "ChannelsRelatedRequestOrder": ".channels_related_request_order", - "OrganizationsRelatedRequestIsAppearance": ".organizations_related_request_is_appearance", - "OrganizationsRelatedRequestMode": ".organizations_related_request_mode", - "OrganizationsRelatedRequestOrder": ".organizations_related_request_order", - "PeopleRelatedRequestIsAppearance": ".people_related_request_is_appearance", - "PeopleRelatedRequestMode": ".people_related_request_mode", - "PeopleRelatedRequestOrder": ".people_related_request_order", - "ProductsRelatedRequestIsAppearance": ".products_related_request_is_appearance", - "ProductsRelatedRequestMode": ".products_related_request_mode", - "ProductsRelatedRequestOrder": ".products_related_request_order", - "TopicsRelatedRequestIsAppearance": ".topics_related_request_is_appearance", - "TopicsRelatedRequestMode": ".topics_related_request_mode", - "TopicsRelatedRequestOrder": ".topics_related_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/channels/related/types/channels_related_request_is_appearance.py b/src/arcmira/channels/related/types/channels_related_request_is_appearance.py deleted file mode 100644 index e21cd2c..0000000 --- a/src/arcmira/channels/related/types/channels_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/related/types/channels_related_request_mode.py b/src/arcmira/channels/related/types/channels_related_request_mode.py deleted file mode 100644 index 49a4137..0000000 --- a/src/arcmira/channels/related/types/channels_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/related/types/channels_related_request_order.py b/src/arcmira/channels/related/types/channels_related_request_order.py deleted file mode 100644 index 0f5b302..0000000 --- a/src/arcmira/channels/related/types/channels_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/related/types/organizations_related_request_is_appearance.py b/src/arcmira/channels/related/types/organizations_related_request_is_appearance.py deleted file mode 100644 index 2762d60..0000000 --- a/src/arcmira/channels/related/types/organizations_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/related/types/organizations_related_request_mode.py b/src/arcmira/channels/related/types/organizations_related_request_mode.py deleted file mode 100644 index 2bdd64b..0000000 --- a/src/arcmira/channels/related/types/organizations_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/related/types/organizations_related_request_order.py b/src/arcmira/channels/related/types/organizations_related_request_order.py deleted file mode 100644 index 4c5dde2..0000000 --- a/src/arcmira/channels/related/types/organizations_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/related/types/people_related_request_is_appearance.py b/src/arcmira/channels/related/types/people_related_request_is_appearance.py deleted file mode 100644 index af591fc..0000000 --- a/src/arcmira/channels/related/types/people_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/related/types/people_related_request_mode.py b/src/arcmira/channels/related/types/people_related_request_mode.py deleted file mode 100644 index 9d9b51c..0000000 --- a/src/arcmira/channels/related/types/people_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/related/types/people_related_request_order.py b/src/arcmira/channels/related/types/people_related_request_order.py deleted file mode 100644 index a0d19ad..0000000 --- a/src/arcmira/channels/related/types/people_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/related/types/products_related_request_is_appearance.py b/src/arcmira/channels/related/types/products_related_request_is_appearance.py deleted file mode 100644 index a6aba02..0000000 --- a/src/arcmira/channels/related/types/products_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/related/types/products_related_request_mode.py b/src/arcmira/channels/related/types/products_related_request_mode.py deleted file mode 100644 index 9046641..0000000 --- a/src/arcmira/channels/related/types/products_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/related/types/products_related_request_order.py b/src/arcmira/channels/related/types/products_related_request_order.py deleted file mode 100644 index 3e5eb9f..0000000 --- a/src/arcmira/channels/related/types/products_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/related/types/topics_related_request_is_appearance.py b/src/arcmira/channels/related/types/topics_related_request_is_appearance.py deleted file mode 100644 index 2b7c65e..0000000 --- a/src/arcmira/channels/related/types/topics_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/channels/related/types/topics_related_request_mode.py b/src/arcmira/channels/related/types/topics_related_request_mode.py deleted file mode 100644 index 090ee69..0000000 --- a/src/arcmira/channels/related/types/topics_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/channels/related/types/topics_related_request_order.py b/src/arcmira/channels/related/types/topics_related_request_order.py deleted file mode 100644 index 56c645a..0000000 --- a/src/arcmira/channels/related/types/topics_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/channels/videos/client.py b/src/arcmira/channels/videos/client.py index 0d81a1f..78dc85a 100644 --- a/src/arcmira/channels/videos/client.py +++ b/src/arcmira/channels/videos/client.py @@ -31,8 +31,8 @@ def list( *, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[ChannelVideosResponseEpisodesItem, ChannelVideosResponse]: """ @@ -49,11 +49,11 @@ def list( cursor : typing.Optional[str] Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. - published_after : typing.Optional[str] - ISO date. Only videos published on or after this day. + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only videos published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -80,12 +80,7 @@ def list( yield page """ return self._raw_client.list( - channel_id, - limit=limit, - cursor=cursor, - published_after=published_after, - published_before=published_before, - request_options=request_options, + channel_id, limit=limit, cursor=cursor, after=after, before=before, request_options=request_options ) @@ -110,8 +105,8 @@ async def list( *, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[ChannelVideosResponseEpisodesItem, ChannelVideosResponse]: """ @@ -128,11 +123,11 @@ async def list( cursor : typing.Optional[str] Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. - published_after : typing.Optional[str] - ISO date. Only videos published on or after this day. + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only videos published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -168,10 +163,5 @@ async def main() -> None: asyncio.run(main()) """ return await self._raw_client.list( - channel_id, - limit=limit, - cursor=cursor, - published_after=published_after, - published_before=published_before, - request_options=request_options, + channel_id, limit=limit, cursor=cursor, after=after, before=before, request_options=request_options ) diff --git a/src/arcmira/channels/videos/raw_client.py b/src/arcmira/channels/videos/raw_client.py index ae833d0..48a9d96 100644 --- a/src/arcmira/channels/videos/raw_client.py +++ b/src/arcmira/channels/videos/raw_client.py @@ -33,8 +33,8 @@ def list( *, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[ChannelVideosResponseEpisodesItem, ChannelVideosResponse]: """ @@ -51,11 +51,11 @@ def list( cursor : typing.Optional[str] Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. - published_after : typing.Optional[str] - ISO date. Only videos published on or after this day. + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only videos published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -71,8 +71,8 @@ def list( params={ "limit": limit, "cursor": cursor, - "published_after": published_after, - "published_before": published_before, + "after": after, + "before": before, }, request_options=request_options, ) @@ -92,8 +92,8 @@ def list( channel_id, limit=limit, cursor=_parsed_next, - published_after=published_after, - published_before=published_before, + after=after, + before=before, request_options=request_options, ) return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) @@ -194,8 +194,8 @@ async def list( *, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[ChannelVideosResponseEpisodesItem, ChannelVideosResponse]: """ @@ -212,11 +212,11 @@ async def list( cursor : typing.Optional[str] Opaque continuation from next_cursor. Bound to the channel, filters, caller, and visibility; limit may change between pages. Invalid or old tokens return invalid_cursor. - published_after : typing.Optional[str] - ISO date. Only videos published on or after this day. + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only videos published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -232,8 +232,8 @@ async def list( params={ "limit": limit, "cursor": cursor, - "published_after": published_after, - "published_before": published_before, + "after": after, + "before": before, }, request_options=request_options, ) @@ -255,8 +255,8 @@ async def _get_next(): channel_id, limit=limit, cursor=_parsed_next, - published_after=published_after, - published_before=published_before, + after=after, + before=before, request_options=request_options, ) diff --git a/src/arcmira/client.py b/src/arcmira/client.py index 384be17..f8fd3e5 100644 --- a/src/arcmira/client.py +++ b/src/arcmira/client.py @@ -12,19 +12,14 @@ if typing.TYPE_CHECKING: from .channels.client import AsyncChannelsClient, ChannelsClient - from .corrections.client import AsyncCorrectionsClient, CorrectionsClient from .entities.client import AsyncEntitiesClient, EntitiesClient from .feedback.client import AsyncFeedbackClient, FeedbackClient from .health.client import AsyncHealthClient, HealthClient + from .integrations.client import AsyncIntegrationsClient, IntegrationsClient from .me.client import AsyncMeClient, MeClient from .mentions.client import AsyncMentionsClient, MentionsClient from .monitors.client import AsyncMonitorsClient, MonitorsClient - from .organizations.client import AsyncOrganizationsClient, OrganizationsClient - from .people.client import AsyncPeopleClient, PeopleClient - from .products.client import AsyncProductsClient, ProductsClient from .recommendations.client import AsyncRecommendationsClient, RecommendationsClient - from .team.client import AsyncTeamClient, TeamClient - from .topics.client import AsyncTopicsClient, TopicsClient from .trackers.client import AsyncTrackersClient, TrackersClient from .transcripts.client import AsyncTranscriptsClient, TranscriptsClient @@ -121,14 +116,9 @@ def __init__( self._feedback: typing.Optional[FeedbackClient] = None self._transcripts: typing.Optional[TranscriptsClient] = None self._channels: typing.Optional[ChannelsClient] = None - self._people: typing.Optional[PeopleClient] = None - self._topics: typing.Optional[TopicsClient] = None - self._organizations: typing.Optional[OrganizationsClient] = None - self._products: typing.Optional[ProductsClient] = None self._monitors: typing.Optional[MonitorsClient] = None self._trackers: typing.Optional[TrackersClient] = None - self._team: typing.Optional[TeamClient] = None - self._corrections: typing.Optional[CorrectionsClient] = None + self._integrations: typing.Optional[IntegrationsClient] = None @property def health(self): @@ -194,38 +184,6 @@ def channels(self): self._channels = ChannelsClient(client_wrapper=self._client_wrapper) return self._channels - @property - def people(self): - if self._people is None: - from .people.client import PeopleClient # noqa: E402 - - self._people = PeopleClient(client_wrapper=self._client_wrapper) - return self._people - - @property - def topics(self): - if self._topics is None: - from .topics.client import TopicsClient # noqa: E402 - - self._topics = TopicsClient(client_wrapper=self._client_wrapper) - return self._topics - - @property - def organizations(self): - if self._organizations is None: - from .organizations.client import OrganizationsClient # noqa: E402 - - self._organizations = OrganizationsClient(client_wrapper=self._client_wrapper) - return self._organizations - - @property - def products(self): - if self._products is None: - from .products.client import ProductsClient # noqa: E402 - - self._products = ProductsClient(client_wrapper=self._client_wrapper) - return self._products - @property def monitors(self): if self._monitors is None: @@ -243,20 +201,12 @@ def trackers(self): return self._trackers @property - def team(self): - if self._team is None: - from .team.client import TeamClient # noqa: E402 - - self._team = TeamClient(client_wrapper=self._client_wrapper) - return self._team - - @property - def corrections(self): - if self._corrections is None: - from .corrections.client import CorrectionsClient # noqa: E402 + def integrations(self): + if self._integrations is None: + from .integrations.client import IntegrationsClient # noqa: E402 - self._corrections = CorrectionsClient(client_wrapper=self._client_wrapper) - return self._corrections + self._integrations = IntegrationsClient(client_wrapper=self._client_wrapper) + return self._integrations def _make_default_async_client( @@ -372,14 +322,9 @@ def __init__( self._feedback: typing.Optional[AsyncFeedbackClient] = None self._transcripts: typing.Optional[AsyncTranscriptsClient] = None self._channels: typing.Optional[AsyncChannelsClient] = None - self._people: typing.Optional[AsyncPeopleClient] = None - self._topics: typing.Optional[AsyncTopicsClient] = None - self._organizations: typing.Optional[AsyncOrganizationsClient] = None - self._products: typing.Optional[AsyncProductsClient] = None self._monitors: typing.Optional[AsyncMonitorsClient] = None self._trackers: typing.Optional[AsyncTrackersClient] = None - self._team: typing.Optional[AsyncTeamClient] = None - self._corrections: typing.Optional[AsyncCorrectionsClient] = None + self._integrations: typing.Optional[AsyncIntegrationsClient] = None @property def health(self): @@ -445,38 +390,6 @@ def channels(self): self._channels = AsyncChannelsClient(client_wrapper=self._client_wrapper) return self._channels - @property - def people(self): - if self._people is None: - from .people.client import AsyncPeopleClient # noqa: E402 - - self._people = AsyncPeopleClient(client_wrapper=self._client_wrapper) - return self._people - - @property - def topics(self): - if self._topics is None: - from .topics.client import AsyncTopicsClient # noqa: E402 - - self._topics = AsyncTopicsClient(client_wrapper=self._client_wrapper) - return self._topics - - @property - def organizations(self): - if self._organizations is None: - from .organizations.client import AsyncOrganizationsClient # noqa: E402 - - self._organizations = AsyncOrganizationsClient(client_wrapper=self._client_wrapper) - return self._organizations - - @property - def products(self): - if self._products is None: - from .products.client import AsyncProductsClient # noqa: E402 - - self._products = AsyncProductsClient(client_wrapper=self._client_wrapper) - return self._products - @property def monitors(self): if self._monitors is None: @@ -494,20 +407,12 @@ def trackers(self): return self._trackers @property - def team(self): - if self._team is None: - from .team.client import AsyncTeamClient # noqa: E402 - - self._team = AsyncTeamClient(client_wrapper=self._client_wrapper) - return self._team - - @property - def corrections(self): - if self._corrections is None: - from .corrections.client import AsyncCorrectionsClient # noqa: E402 + def integrations(self): + if self._integrations is None: + from .integrations.client import AsyncIntegrationsClient # noqa: E402 - self._corrections = AsyncCorrectionsClient(client_wrapper=self._client_wrapper) - return self._corrections + self._integrations = AsyncIntegrationsClient(client_wrapper=self._client_wrapper) + return self._integrations def _get_base_url(*, base_url: typing.Optional[str] = None, environment: ArcmiraEnvironment) -> str: diff --git a/src/arcmira/core/client_wrapper.py b/src/arcmira/core/client_wrapper.py index d359092..61542a6 100644 --- a/src/arcmira/core/client_wrapper.py +++ b/src/arcmira/core/client_wrapper.py @@ -33,7 +33,7 @@ def get_headers(self) -> typing.Dict[str, str]: import platform headers: typing.Dict[str, str] = { - "User-Agent": "arcmira/0.3.0", + "User-Agent": "arcmira/0.4.0", "X-Fern-Language": "Python", "X-Fern-Runtime": f"python/{platform.python_version()}", "X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}", diff --git a/src/arcmira/corrections/__init__.py b/src/arcmira/corrections/__init__.py deleted file mode 100644 index e74377b..0000000 --- a/src/arcmira/corrections/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import SubmitCorrectionsRequestAnchor, SubmitCorrectionsRequestKind -_dynamic_imports: typing.Dict[str, str] = { - "SubmitCorrectionsRequestAnchor": ".types", - "SubmitCorrectionsRequestKind": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["SubmitCorrectionsRequestAnchor", "SubmitCorrectionsRequestKind"] diff --git a/src/arcmira/corrections/client.py b/src/arcmira/corrections/client.py deleted file mode 100644 index 7c7cd7f..0000000 --- a/src/arcmira/corrections/client.py +++ /dev/null @@ -1,406 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.correction_accepted_response import CorrectionAcceptedResponse -from ..types.withdrawn_response import WithdrawnResponse -from .raw_client import AsyncRawCorrectionsClient, RawCorrectionsClient -from .types.submit_corrections_request_anchor import SubmitCorrectionsRequestAnchor -from .types.submit_corrections_request_kind import SubmitCorrectionsRequestKind - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class CorrectionsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawCorrectionsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawCorrectionsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawCorrectionsClient - """ - return self._raw_client - - def submit( - self, - video_id: str, - *, - kind: SubmitCorrectionsRequestKind, - payload: typing.Dict[str, typing.Any], - idempotency_key: typing.Optional[str] = None, - seq: typing.Optional[int] = OMIT, - revision: typing.Optional[str] = OMIT, - anchor: typing.Optional[SubmitCorrectionsRequestAnchor] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> CorrectionAcceptedResponse: - """ - Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - kind : SubmitCorrectionsRequestKind - - payload : typing.Dict[str, typing.Any] - Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once. - - seq : typing.Optional[int] - Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value. - - revision : typing.Optional[str] - The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read. - - anchor : typing.Optional[SubmitCorrectionsRequestAnchor] - Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CorrectionAcceptedResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.corrections.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - kind="line_edit", - payload={"key": "value"}, - ) - """ - _response = self._raw_client.submit( - video_id, - kind=kind, - payload=payload, - idempotency_key=idempotency_key, - seq=seq, - revision=revision, - anchor=anchor, - request_options=request_options, - ) - return _response.data - - def withdraw_speaker_edit( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.corrections.withdraw_speaker_edit( - id="id", - ) - """ - _response = self._raw_client.withdraw_speaker_edit(id, request_options=request_options) - return _response.data - - def withdraw_entity_tag( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.corrections.withdraw_entity_tag( - id="id", - ) - """ - _response = self._raw_client.withdraw_entity_tag(id, request_options=request_options) - return _response.data - - def withdraw_segment_rewrite( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.corrections.withdraw_segment_rewrite( - id="id", - ) - """ - _response = self._raw_client.withdraw_segment_rewrite(id, request_options=request_options) - return _response.data - - -class AsyncCorrectionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawCorrectionsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawCorrectionsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawCorrectionsClient - """ - return self._raw_client - - async def submit( - self, - video_id: str, - *, - kind: SubmitCorrectionsRequestKind, - payload: typing.Dict[str, typing.Any], - idempotency_key: typing.Optional[str] = None, - seq: typing.Optional[int] = OMIT, - revision: typing.Optional[str] = OMIT, - anchor: typing.Optional[SubmitCorrectionsRequestAnchor] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> CorrectionAcceptedResponse: - """ - Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - kind : SubmitCorrectionsRequestKind - - payload : typing.Dict[str, typing.Any] - Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once. - - seq : typing.Optional[int] - Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value. - - revision : typing.Optional[str] - The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read. - - anchor : typing.Optional[SubmitCorrectionsRequestAnchor] - Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CorrectionAcceptedResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.corrections.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - kind="line_edit", - payload={"key": "value"}, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.submit( - video_id, - kind=kind, - payload=payload, - idempotency_key=idempotency_key, - seq=seq, - revision=revision, - anchor=anchor, - request_options=request_options, - ) - return _response.data - - async def withdraw_speaker_edit( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.corrections.withdraw_speaker_edit( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw_speaker_edit(id, request_options=request_options) - return _response.data - - async def withdraw_entity_tag( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.corrections.withdraw_entity_tag( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw_entity_tag(id, request_options=request_options) - return _response.data - - async def withdraw_segment_rewrite( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.corrections.withdraw_segment_rewrite( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw_segment_rewrite(id, request_options=request_options) - return _response.data diff --git a/src/arcmira/corrections/raw_client.py b/src/arcmira/corrections/raw_client.py deleted file mode 100644 index 7b19b7a..0000000 --- a/src/arcmira/corrections/raw_client.py +++ /dev/null @@ -1,1024 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import encode_path_param -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..core.serialization import convert_and_respect_annotation_metadata -from ..errors.bad_request_error import BadRequestError -from ..errors.conflict_error import ConflictError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.precondition_failed_error import PreconditionFailedError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.correction_accepted_response import CorrectionAcceptedResponse -from ..types.error import Error -from ..types.withdrawn_response import WithdrawnResponse -from .types.submit_corrections_request_anchor import SubmitCorrectionsRequestAnchor -from .types.submit_corrections_request_kind import SubmitCorrectionsRequestKind -from pydantic import ValidationError - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawCorrectionsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def submit( - self, - video_id: str, - *, - kind: SubmitCorrectionsRequestKind, - payload: typing.Dict[str, typing.Any], - idempotency_key: typing.Optional[str] = None, - seq: typing.Optional[int] = OMIT, - revision: typing.Optional[str] = OMIT, - anchor: typing.Optional[SubmitCorrectionsRequestAnchor] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[CorrectionAcceptedResponse]: - """ - Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - kind : SubmitCorrectionsRequestKind - - payload : typing.Dict[str, typing.Any] - Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once. - - seq : typing.Optional[int] - Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value. - - revision : typing.Optional[str] - The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read. - - anchor : typing.Optional[SubmitCorrectionsRequestAnchor] - Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[CorrectionAcceptedResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/videos/{encode_path_param(video_id)}/corrections", - method="POST", - json={ - "kind": kind, - "seq": seq, - "revision": revision, - "anchor": convert_and_respect_annotation_metadata( - object_=anchor, annotation=SubmitCorrectionsRequestAnchor, direction="write" - ), - "payload": payload, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - CorrectionAcceptedResponse, - parse_obj_as( - type_=CorrectionAcceptedResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 412: - raise PreconditionFailedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw_speaker_edit( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/corrections/speaker-edits/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw_entity_tag( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/corrections/entity-tags/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw_segment_rewrite( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/corrections/segment-rewrites/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawCorrectionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def submit( - self, - video_id: str, - *, - kind: SubmitCorrectionsRequestKind, - payload: typing.Dict[str, typing.Any], - idempotency_key: typing.Optional[str] = None, - seq: typing.Optional[int] = OMIT, - revision: typing.Optional[str] = OMIT, - anchor: typing.Optional[SubmitCorrectionsRequestAnchor] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[CorrectionAcceptedResponse]: - """ - Unified corrections ingestion for all kinds: line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite. Corrections are free (0 rows) and land as pending-review rows attributed to your API key; speaker_identify/add_person also create a community-flagged appearance immediately. segment_rewrite is the structural primitive: it replaces an inclusive segment range with new segments (an empty replacements array deletes the range); timestamps can be pinned per replacement with optional start/end seconds, and unpinned times are repaired by char-proportional interpolation between pins. Anchored kinds (line_edit, speaker_reassign, entity_tag, segment_rewrite) must echo the `revision` from a Premium transcript read and an `anchor` ({ segmentIndex, contentHash: djb2 of the covered segment text }); `anchor.segmentIndex` is the line's `index` in that read. Every refusal uses the shared error envelope. Error semantics for outbox-style clients: a 409 revision_mismatch or anchor_mismatch means the transcript changed, and error.current_revision is the revision to re-read; create a new event and key after re-anchoring, because the original refusal consumed its sequence and is replayable. A 409 idempotency_conflict means a finalized key was reused for changed intent; recover the original request instead of rebasing that key. A 412 sequence_mismatch stores no receipt or effect; error.expected_seq is the next seq, so synchronize local counters and resend under the same key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - kind : SubmitCorrectionsRequestKind - - payload : typing.Dict[str, typing.Any] - Kind-specific payload. line_edit: { segmentIndex, originalText, correctedText }. speaker_reassign: { selection: { startIndex, startChar, endIndex, endChar }, target: { kind: existing|new|role, speakerId, label?, role? } }. speaker_identify: { speakerId, entityId }. add_person: { speakerId, name }. entity_tag: { segmentIndex, charStart, charEnd, entityId } or { segmentIndex, charStart, charEnd, proposedName, proposedType }. segment_rewrite: { startIndex, endIndex, replacements: [{ text, speaker?, start?, end? }] }. replaces the inclusive segment range with the replacements (max 50 source segments, 50 replacements, 2000 chars each); an empty array deletes the range. Timestamps: optional start/end pins (seconds, non-decreasing across the list) fix times explicitly; every unpinned time is interpolated char-proportionally between the surrounding pins (outer bounds default to the source time range). A replacement without speaker inherits from the source segment its repaired start time falls in; startIndex must equal anchor.segmentIndex and the anchor contentHash covers startIndex through endIndex. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique client event key with the HTTP method, public path and parsed body. The same finalized intent replays its original response with Idempotency-Replayed: true; changed finalized input returns 409 idempotency_conflict. Receipts have no expiry. A 412 stores no receipt or effect, so synchronize the sequence and resend under the same key. Other finalized refusals consume the eligible sequence once. - - seq : typing.Optional[int] - Per-video monotonic sequence number (strict FIFO per user+video). Optional for one-off submissions; required for outbox-style clients that depend on ordering. Any mismatch returns 412 with the expected value. - - revision : typing.Optional[str] - The revision from the Premium transcript read this correction was made against. Required for every kind. Speaker ids in the payload are the speakers[].id values of that read. - - anchor : typing.Optional[SubmitCorrectionsRequestAnchor] - Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[CorrectionAcceptedResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/videos/{encode_path_param(video_id)}/corrections", - method="POST", - json={ - "kind": kind, - "seq": seq, - "revision": revision, - "anchor": convert_and_respect_annotation_metadata( - object_=anchor, annotation=SubmitCorrectionsRequestAnchor, direction="write" - ), - "payload": payload, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - CorrectionAcceptedResponse, - parse_obj_as( - type_=CorrectionAcceptedResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 412: - raise PreconditionFailedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw_speaker_edit( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/corrections/speaker-edits/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw_entity_tag( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/corrections/entity-tags/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw_segment_rewrite( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/corrections/segment-rewrites/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/corrections/types/__init__.py b/src/arcmira/corrections/types/__init__.py deleted file mode 100644 index 17c31dd..0000000 --- a/src/arcmira/corrections/types/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .submit_corrections_request_anchor import SubmitCorrectionsRequestAnchor - from .submit_corrections_request_kind import SubmitCorrectionsRequestKind -_dynamic_imports: typing.Dict[str, str] = { - "SubmitCorrectionsRequestAnchor": ".submit_corrections_request_anchor", - "SubmitCorrectionsRequestKind": ".submit_corrections_request_kind", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["SubmitCorrectionsRequestAnchor", "SubmitCorrectionsRequestKind"] diff --git a/src/arcmira/corrections/types/submit_corrections_request_anchor.py b/src/arcmira/corrections/types/submit_corrections_request_anchor.py deleted file mode 100644 index 0df5e43..0000000 --- a/src/arcmira/corrections/types/submit_corrections_request_anchor.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ...core.serialization import FieldMetadata - - -class SubmitCorrectionsRequestAnchor(UniversalBaseModel): - """ - Required for line_edit, speaker_reassign, entity_tag, and segment_rewrite. - """ - - segment_index: typing_extensions.Annotated[ - int, FieldMetadata(alias="segmentIndex"), pydantic.Field(alias="segmentIndex") - ] - content_hash: typing_extensions.Annotated[ - str, - FieldMetadata(alias="contentHash"), - pydantic.Field( - alias="contentHash", - description="djb2 hash of the covered segment text (joined with \\n for ranges), computed against your projected view of the transcript.", - ), - ] - """ - djb2 hash of the covered segment text (joined with \\n for ranges), computed against your projected view of the transcript. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/corrections/types/submit_corrections_request_kind.py b/src/arcmira/corrections/types/submit_corrections_request_kind.py deleted file mode 100644 index 2c148b1..0000000 --- a/src/arcmira/corrections/types/submit_corrections_request_kind.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SubmitCorrectionsRequestKind = typing.Union[ - typing.Literal["line_edit", "speaker_reassign", "speaker_identify", "add_person", "entity_tag", "segment_rewrite"], - typing.Any, -] diff --git a/src/arcmira/entities/__init__.py b/src/arcmira/entities/__init__.py index 4d0f946..d6a0411 100644 --- a/src/arcmira/entities/__init__.py +++ b/src/arcmira/entities/__init__.py @@ -6,20 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import LookupEntitiesRequestType, ResolveEntitiesRequestType, SearchEntitiesRequestType - from . import mentions, recommendations - from .mentions import ListMentionsRequestDetails, ListMentionsRequestSentiment - from .recommendations import ListRecommendationsRequestMentionClass -_dynamic_imports: typing.Dict[str, str] = { - "ListMentionsRequestDetails": ".mentions", - "ListMentionsRequestSentiment": ".mentions", - "ListRecommendationsRequestMentionClass": ".recommendations", - "LookupEntitiesRequestType": ".types", - "ResolveEntitiesRequestType": ".types", - "SearchEntitiesRequestType": ".types", - "mentions": ".mentions", - "recommendations": ".recommendations", -} + from .types import ResolveEntitiesRequestType +_dynamic_imports: typing.Dict[str, str] = {"ResolveEntitiesRequestType": ".types"} def __getattr__(attr_name: str) -> typing.Any: @@ -43,13 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = [ - "ListMentionsRequestDetails", - "ListMentionsRequestSentiment", - "ListRecommendationsRequestMentionClass", - "LookupEntitiesRequestType", - "ResolveEntitiesRequestType", - "SearchEntitiesRequestType", - "mentions", - "recommendations", -] +__all__ = ["ResolveEntitiesRequestType"] diff --git a/src/arcmira/entities/client.py b/src/arcmira/entities/client.py index b14745e..fe25e1b 100644 --- a/src/arcmira/entities/client.py +++ b/src/arcmira/entities/client.py @@ -1,33 +1,19 @@ # This file was auto-generated by Fern from our API Definition. -from __future__ import annotations - import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.request_options import RequestOptions -from ..types.entity_cards_response import EntityCardsResponse from ..types.entity_detail_response import EntityDetailResponse -from ..types.entity_lookup_response import EntityLookupResponse from ..types.entity_momentum_response import EntityMomentumResponse from ..types.entity_resolve_response import EntityResolveResponse -from ..types.entity_search_response import EntitySearchResponse from .raw_client import AsyncRawEntitiesClient, RawEntitiesClient -from .types.lookup_entities_request_type import LookupEntitiesRequestType from .types.resolve_entities_request_type import ResolveEntitiesRequestType -from .types.search_entities_request_type import SearchEntitiesRequestType - -if typing.TYPE_CHECKING: - from .mentions.client import AsyncMentionsClient, MentionsClient - from .recommendations.client import AsyncRecommendationsClient, RecommendationsClient class EntitiesClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawEntitiesClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._mentions: typing.Optional[MentionsClient] = None - self._recommendations: typing.Optional[RecommendationsClient] = None @property def with_raw_response(self) -> RawEntitiesClient: @@ -40,56 +26,6 @@ def with_raw_response(self) -> RawEntitiesClient: """ return self._raw_client - def search( - self, - *, - q: str, - type: typing.Optional[SearchEntitiesRequestType] = None, - has_recommendations_data: typing.Optional[bool] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntitySearchResponse: - """ - Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists. - - Parameters - ---------- - q : str - - type : typing.Optional[SearchEntitiesRequestType] - - has_recommendations_data : typing.Optional[bool] - - limit : typing.Optional[int] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntitySearchResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.entities.search( - q="q", - ) - """ - _response = self._raw_client.search( - q=q, - type=type, - has_recommendations_data=has_recommendations_data, - limit=limit, - request_options=request_options, - ) - return _response.data - def resolve( self, *, @@ -140,76 +76,6 @@ def resolve( ) return _response.data - def lookup( - self, - *, - id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - type: typing.Optional[LookupEntitiesRequestType] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntityLookupResponse: - """ - Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type. - - Parameters - ---------- - id : typing.Optional[str] - - name : typing.Optional[str] - - type : typing.Optional[LookupEntitiesRequestType] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityLookupResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.entities.lookup() - """ - _response = self._raw_client.lookup(id=id, name=name, type=type, request_options=request_options) - return _response.data - - def cards(self, *, ids: str, request_options: typing.Optional[RequestOptions] = None) -> EntityCardsResponse: - """ - Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable. - - Parameters - ---------- - ids : str - Comma-separated raw integer entity ids, 1 to 50 of them (e.g. "12,844,1032"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityCardsResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.entities.cards( - ids="ids", - ) - """ - _response = self._raw_client.cards(ids=ids, request_options=request_options) - return _response.data - def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityDetailResponse: """ Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists. @@ -272,29 +138,10 @@ def momentum(self, id: str, *, request_options: typing.Optional[RequestOptions] _response = self._raw_client.momentum(id, request_options=request_options) return _response.data - @property - def mentions(self): - if self._mentions is None: - from .mentions.client import MentionsClient # noqa: E402 - - self._mentions = MentionsClient(client_wrapper=self._client_wrapper) - return self._mentions - - @property - def recommendations(self): - if self._recommendations is None: - from .recommendations.client import RecommendationsClient # noqa: E402 - - self._recommendations = RecommendationsClient(client_wrapper=self._client_wrapper) - return self._recommendations - class AsyncEntitiesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._raw_client = AsyncRawEntitiesClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._mentions: typing.Optional[AsyncMentionsClient] = None - self._recommendations: typing.Optional[AsyncRecommendationsClient] = None @property def with_raw_response(self) -> AsyncRawEntitiesClient: @@ -307,64 +154,6 @@ def with_raw_response(self) -> AsyncRawEntitiesClient: """ return self._raw_client - async def search( - self, - *, - q: str, - type: typing.Optional[SearchEntitiesRequestType] = None, - has_recommendations_data: typing.Optional[bool] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntitySearchResponse: - """ - Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists. - - Parameters - ---------- - q : str - - type : typing.Optional[SearchEntitiesRequestType] - - has_recommendations_data : typing.Optional[bool] - - limit : typing.Optional[int] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntitySearchResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.entities.search( - q="q", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.search( - q=q, - type=type, - has_recommendations_data=has_recommendations_data, - limit=limit, - request_options=request_options, - ) - return _response.data - async def resolve( self, *, @@ -423,92 +212,6 @@ async def main() -> None: ) return _response.data - async def lookup( - self, - *, - id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - type: typing.Optional[LookupEntitiesRequestType] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> EntityLookupResponse: - """ - Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type. - - Parameters - ---------- - id : typing.Optional[str] - - name : typing.Optional[str] - - type : typing.Optional[LookupEntitiesRequestType] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityLookupResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.entities.lookup() - - - asyncio.run(main()) - """ - _response = await self._raw_client.lookup(id=id, name=name, type=type, request_options=request_options) - return _response.data - - async def cards(self, *, ids: str, request_options: typing.Optional[RequestOptions] = None) -> EntityCardsResponse: - """ - Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable. - - Parameters - ---------- - ids : str - Comma-separated raw integer entity ids, 1 to 50 of them (e.g. "12,844,1032"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - EntityCardsResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.entities.cards( - ids="ids", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.cards(ids=ids, request_options=request_options) - return _response.data - async def get(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> EntityDetailResponse: """ Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists. @@ -588,19 +291,3 @@ async def main() -> None: """ _response = await self._raw_client.momentum(id, request_options=request_options) return _response.data - - @property - def mentions(self): - if self._mentions is None: - from .mentions.client import AsyncMentionsClient # noqa: E402 - - self._mentions = AsyncMentionsClient(client_wrapper=self._client_wrapper) - return self._mentions - - @property - def recommendations(self): - if self._recommendations is None: - from .recommendations.client import AsyncRecommendationsClient # noqa: E402 - - self._recommendations = AsyncRecommendationsClient(client_wrapper=self._client_wrapper) - return self._recommendations diff --git a/src/arcmira/entities/mentions/client.py b/src/arcmira/entities/mentions/client.py deleted file mode 100644 index 42b314f..0000000 --- a/src/arcmira/entities/mentions/client.py +++ /dev/null @@ -1,221 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.mention import Mention -from ...types.mention_list_response import MentionListResponse -from .raw_client import AsyncRawMentionsClient, RawMentionsClient -from .types.list_mentions_request_details import ListMentionsRequestDetails -from .types.list_mentions_request_sentiment import ListMentionsRequestSentiment - - -class MentionsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawMentionsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawMentionsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawMentionsClient - """ - return self._raw_client - - def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - q: typing.Optional[str] = None, - sentiment: typing.Optional[ListMentionsRequestSentiment] = None, - is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - details: typing.Optional[ListMentionsRequestDetails] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Mention, MentionListResponse]: - """ - Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - q : typing.Optional[str] - - sentiment : typing.Optional[ListMentionsRequestSentiment] - - is_appearance : typing.Optional[bool] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - details : typing.Optional[ListMentionsRequestDetails] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[Mention, MentionListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.entities.mentions.list( - id="id", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.list( - id, - limit=limit, - cursor=cursor, - channel_id=channel_id, - channel_name=channel_name, - q=q, - sentiment=sentiment, - is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, - details=details, - request_options=request_options, - ) - - -class AsyncMentionsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawMentionsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawMentionsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawMentionsClient - """ - return self._raw_client - - async def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - q: typing.Optional[str] = None, - sentiment: typing.Optional[ListMentionsRequestSentiment] = None, - is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - details: typing.Optional[ListMentionsRequestDetails] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Mention, MentionListResponse]: - """ - Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - q : typing.Optional[str] - - sentiment : typing.Optional[ListMentionsRequestSentiment] - - is_appearance : typing.Optional[bool] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - details : typing.Optional[ListMentionsRequestDetails] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[Mention, MentionListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.entities.mentions.list( - id="id", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.list( - id, - limit=limit, - cursor=cursor, - channel_id=channel_id, - channel_name=channel_name, - q=q, - sentiment=sentiment, - is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, - details=details, - request_options=request_options, - ) diff --git a/src/arcmira/entities/mentions/types/__init__.py b/src/arcmira/entities/mentions/types/__init__.py deleted file mode 100644 index 274ea68..0000000 --- a/src/arcmira/entities/mentions/types/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .list_mentions_request_details import ListMentionsRequestDetails - from .list_mentions_request_sentiment import ListMentionsRequestSentiment -_dynamic_imports: typing.Dict[str, str] = { - "ListMentionsRequestDetails": ".list_mentions_request_details", - "ListMentionsRequestSentiment": ".list_mentions_request_sentiment", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListMentionsRequestDetails", "ListMentionsRequestSentiment"] diff --git a/src/arcmira/entities/mentions/types/list_mentions_request_sentiment.py b/src/arcmira/entities/mentions/types/list_mentions_request_sentiment.py deleted file mode 100644 index 65a4df7..0000000 --- a/src/arcmira/entities/mentions/types/list_mentions_request_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListMentionsRequestSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/entities/raw_client.py b/src/arcmira/entities/raw_client.py index dc355db..4fb0210 100644 --- a/src/arcmira/entities/raw_client.py +++ b/src/arcmira/entities/raw_client.py @@ -17,16 +17,11 @@ from ..errors.payment_required_error import PaymentRequiredError from ..errors.too_many_requests_error import TooManyRequestsError from ..errors.unauthorized_error import UnauthorizedError -from ..types.entity_cards_response import EntityCardsResponse from ..types.entity_detail_response import EntityDetailResponse -from ..types.entity_lookup_response import EntityLookupResponse from ..types.entity_momentum_response import EntityMomentumResponse from ..types.entity_resolve_response import EntityResolveResponse -from ..types.entity_search_response import EntitySearchResponse from ..types.error import Error -from .types.lookup_entities_request_type import LookupEntitiesRequestType from .types.resolve_entities_request_type import ResolveEntitiesRequestType -from .types.search_entities_request_type import SearchEntitiesRequestType from pydantic import ValidationError @@ -34,143 +29,6 @@ class RawEntitiesClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def search( - self, - *, - q: str, - type: typing.Optional[SearchEntitiesRequestType] = None, - has_recommendations_data: typing.Optional[bool] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EntitySearchResponse]: - """ - Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists. - - Parameters - ---------- - q : str - - type : typing.Optional[SearchEntitiesRequestType] - - has_recommendations_data : typing.Optional[bool] - - limit : typing.Optional[int] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[EntitySearchResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - "v1/entities/search", - method="GET", - params={ - "q": q, - "type": type, - "has_recommendations_data": has_recommendations_data, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntitySearchResponse, - parse_obj_as( - type_=EntitySearchResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def resolve( self, *, @@ -301,49 +159,36 @@ def resolve( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def lookup( - self, - *, - id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - type: typing.Optional[LookupEntitiesRequestType] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[EntityLookupResponse]: + def get( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EntityDetailResponse]: """ - Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type. + Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists. Parameters ---------- - id : typing.Optional[str] - - name : typing.Optional[str] - - type : typing.Optional[LookupEntitiesRequestType] + id : str + Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityLookupResponse] + HttpResponse[EntityDetailResponse] Success """ _response = self._client_wrapper.httpx_client.request( - "v1/entities/lookup", + f"v1/entities/{encode_path_param(id)}", method="GET", - params={ - "id": id, - "name": name, - "type": type, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityLookupResponse, + EntityDetailResponse, parse_obj_as( - type_=EntityLookupResponse, # type: ignore + type_=EntityDetailResponse, # type: ignore object_=_response.json(), ), ) @@ -434,39 +279,36 @@ def lookup( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def cards( - self, *, ids: str, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[EntityCardsResponse]: + def momentum( + self, id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[EntityMomentumResponse]: """ - Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable. + Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- - ids : str - Comma-separated raw integer entity ids, 1 to 50 of them (e.g. "12,844,1032"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response. + id : str + Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityCardsResponse] + HttpResponse[EntityMomentumResponse] Success """ _response = self._client_wrapper.httpx_client.request( - "v1/entities/cards", + f"v1/entities/{encode_path_param(id)}/momentum", method="GET", - params={ - "ids": ids, - }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityCardsResponse, + EntityMomentumResponse, parse_obj_as( - type_=EntityCardsResponse, # type: ignore + type_=EntityMomentumResponse, # type: ignore object_=_response.json(), ), ) @@ -493,6 +335,17 @@ def cards( ), ), ) + if _response.status_code == 402: + raise PaymentRequiredError( + headers=dict(_response.headers), + body=typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 403: raise ForbiddenError( headers=dict(_response.headers), @@ -546,437 +399,60 @@ def cards( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def get( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[EntityDetailResponse]: + +class AsyncRawEntitiesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def resolve( + self, + *, + q: str, + type: typing.Optional[ResolveEntitiesRequestType] = None, + limit: typing.Optional[int] = None, + context: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> AsyncHttpResponse[EntityResolveResponse]: """ - Returns the canonical entity envelope for an ent_{n} or numeric id, following merge redirects. For organization and product entities, callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary commercial-intelligence rollup when a brand profile exists. + Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name ("the startup bank", "on My First Million"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows. Parameters ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. + q : str + A name, @handle, YouTube URL or channel id (UC...). One thing per call. + + type : typing.Optional[ResolveEntitiesRequestType] + Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id. + + limit : typing.Optional[int] + Candidates to return, 1 to 15. Default 8. + + context : typing.Optional[str] + What the user said about the name, in their words ("the startup bank", "Canada's prime minister", "on My First Million"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[EntityDetailResponse] + AsyncHttpResponse[EntityResolveResponse] Success """ - _response = self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}", + _response = await self._client_wrapper.httpx_client.request( + "v1/entities/resolve", method="GET", + params={ + "q": q, + "type": type, + "limit": limit, + "context": context, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - EntityDetailResponse, - parse_obj_as( - type_=EntityDetailResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def momentum( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[EntityMomentumResponse]: - """ - Mentions in the last 7 and 30 days against the prior 30, an absolute-delta verdict (accelerating, flat, fading, none), the newest media date, and the top shows in the window. It counts the shows we index, not the whole internet, and it is a count, not a score. On a Pro+ plan the card also carries paid_vs_organic; otherwise that field is absent and access names the gate. Bills one row. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[EntityMomentumResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}/momentum", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityMomentumResponse, - parse_obj_as( - type_=EntityMomentumResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawEntitiesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def search( - self, - *, - q: str, - type: typing.Optional[SearchEntitiesRequestType] = None, - has_recommendations_data: typing.Optional[bool] = None, - limit: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntitySearchResponse]: - """ - Substring name search returning up to 25 entities ordered by appearance count, each with a suggested flag: true on an exact match with far more traction than any other row of its type. A q that is a YouTube channel id (UC...), an @handle, or a YouTube URL carrying either resolves to that one channel row, suggested, with its youtube_channel_id. Callers with Recommendations API access (a Pro+ plan) also receive a recommendations_summary per result when a brand profile exists. - - Parameters - ---------- - q : str - - type : typing.Optional[SearchEntitiesRequestType] - - has_recommendations_data : typing.Optional[bool] - - limit : typing.Optional[int] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EntitySearchResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/entities/search", - method="GET", - params={ - "q": q, - "type": type, - "has_recommendations_data": has_recommendations_data, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntitySearchResponse, - parse_obj_as( - type_=EntitySearchResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def resolve( - self, - *, - q: str, - type: typing.Optional[ResolveEntitiesRequestType] = None, - limit: typing.Optional[int] = None, - context: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntityResolveResponse]: - """ - Call this before passing an id to about, by, entity_ids, channel_ids or channel; those filters refuse names with id_required. Pass context with the user's own words about the name ("the startup bank", "on My First Million"). The answer is one of three: best (the name means one row: use it and name it), suggested (no row is certain but one stands out, with reason and evidence: use it and tell the user you assumed it), or ask (several rows fit: show ask.options, or check every option id and answer per row). For a show pass type=channel and use the youtube_channel_id; for a brand or a person use the id. Free; bills no rows. - - Parameters - ---------- - q : str - A name, @handle, YouTube URL or channel id (UC...). One thing per call. - - type : typing.Optional[ResolveEntitiesRequestType] - Restrict candidates to one type. Pass channel for a show and read best.youtube_channel_id. - - limit : typing.Optional[int] - Candidates to return, 1 to 15. Default 8. - - context : typing.Optional[str] - What the user said about the name, in their words ("the startup bank", "Canada's prime minister", "on My First Million"). Ranks candidates by their description and by the episodes they share with what the context names; a clear winner comes back as suggested with reason context. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EntityResolveResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/entities/resolve", - method="GET", - params={ - "q": q, - "type": type, - "limit": limit, - "context": context, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityResolveResponse, + EntityResolveResponse, parse_obj_as( type_=EntityResolveResponse, # type: ignore object_=_response.json(), @@ -1058,251 +534,6 @@ async def resolve( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - async def lookup( - self, - *, - id: typing.Optional[str] = None, - name: typing.Optional[str] = None, - type: typing.Optional[LookupEntitiesRequestType] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[EntityLookupResponse]: - """ - Resolves an id or name to the canonical entity record, following merge redirects. Pass either id (ent_{n} or numeric) or name, optionally constrained by type. - - Parameters - ---------- - id : typing.Optional[str] - - name : typing.Optional[str] - - type : typing.Optional[LookupEntitiesRequestType] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EntityLookupResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/entities/lookup", - method="GET", - params={ - "id": id, - "name": name, - "type": type, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityLookupResponse, - parse_obj_as( - type_=EntityLookupResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def cards( - self, *, ids: str, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[EntityCardsResponse]: - """ - Free endpoint (0 rows). Batched compact lookups for UI hover cards: name, slug, type, image, and index counts for up to 50 raw integer entity ids per request. Merged ids resolve to their canonical entity but are returned under the requested id; unknown ids are silently dropped. The response is identical for all viewers and CDN-cacheable. - - Parameters - ---------- - ids : str - Comma-separated raw integer entity ids, 1 to 50 of them (e.g. "12,844,1032"). Merged ids resolve to their canonical entity; unknown ids are silently dropped from the response. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[EntityCardsResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/entities/cards", - method="GET", - params={ - "ids": ids, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - EntityCardsResponse, - parse_obj_as( - type_=EntityCardsResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - async def get( self, id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[EntityDetailResponse]: diff --git a/src/arcmira/entities/recommendations/client.py b/src/arcmira/entities/recommendations/client.py deleted file mode 100644 index 25fc62c..0000000 --- a/src/arcmira/entities/recommendations/client.py +++ /dev/null @@ -1,212 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.recommendation import Recommendation -from ...types.recommendation_list_response import RecommendationListResponse -from .raw_client import AsyncRawRecommendationsClient, RawRecommendationsClient -from .types.list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass - - -class RecommendationsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRecommendationsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRecommendationsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRecommendationsClient - """ - return self._raw_client - - def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, - min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - include_disputed: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Recommendation, RecommendationListResponse]: - """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] - - min_confidence : typing.Optional[float] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - include_disputed : typing.Optional[bool] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[Recommendation, RecommendationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.entities.recommendations.list( - id="id", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.list( - id, - limit=limit, - cursor=cursor, - channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, - min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, - include_disputed=include_disputed, - request_options=request_options, - ) - - -class AsyncRecommendationsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRecommendationsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRecommendationsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRecommendationsClient - """ - return self._raw_client - - async def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, - min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - include_disputed: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Recommendation, RecommendationListResponse]: - """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] - - min_confidence : typing.Optional[float] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - include_disputed : typing.Optional[bool] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[Recommendation, RecommendationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.entities.recommendations.list( - id="id", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.list( - id, - limit=limit, - cursor=cursor, - channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, - min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, - include_disputed=include_disputed, - request_options=request_options, - ) diff --git a/src/arcmira/entities/recommendations/raw_client.py b/src/arcmira/entities/recommendations/raw_client.py deleted file mode 100644 index 6815d72..0000000 --- a/src/arcmira/entities/recommendations/raw_client.py +++ /dev/null @@ -1,393 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.recommendation import Recommendation -from ...types.recommendation_list_response import RecommendationListResponse -from .types.list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass -from pydantic import ValidationError - - -class RawRecommendationsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, - min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - include_disputed: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Recommendation, RecommendationListResponse]: - """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] - - min_confidence : typing.Optional[float] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - include_disputed : typing.Optional[bool] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[Recommendation, RecommendationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}/recommendations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "channel_id": channel_id, - "channel_name": channel_name, - "mention_class": mention_class, - "min_confidence": min_confidence, - "date_from": date_from, - "date_to": date_to, - "include_disputed": include_disputed, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - RecommendationListResponse, - parse_obj_as( - type_=RecommendationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list( - id, - limit=limit, - cursor=_parsed_next, - channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, - min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, - include_disputed=include_disputed, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRecommendationsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - id: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, - min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - include_disputed: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Recommendation, RecommendationListResponse]: - """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) for one entity, newest media first. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. Rows below min_confidence (default 0.7) and disputed rows (unless include_disputed=true) are excluded. - - Parameters - ---------- - id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] - - min_confidence : typing.Optional[float] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - include_disputed : typing.Optional[bool] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[Recommendation, RecommendationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}/recommendations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "channel_id": channel_id, - "channel_name": channel_name, - "mention_class": mention_class, - "min_confidence": min_confidence, - "date_from": date_from, - "date_to": date_to, - "include_disputed": include_disputed, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - RecommendationListResponse, - parse_obj_as( - type_=RecommendationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.list( - id, - limit=limit, - cursor=_parsed_next, - channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, - min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, - include_disputed=include_disputed, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/entities/recommendations/types/__init__.py b/src/arcmira/entities/recommendations/types/__init__.py deleted file mode 100644 index 4df49ab..0000000 --- a/src/arcmira/entities/recommendations/types/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass -_dynamic_imports: typing.Dict[str, str] = { - "ListRecommendationsRequestMentionClass": ".list_recommendations_request_mention_class" -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListRecommendationsRequestMentionClass"] diff --git a/src/arcmira/entities/recommendations/types/list_recommendations_request_mention_class.py b/src/arcmira/entities/recommendations/types/list_recommendations_request_mention_class.py deleted file mode 100644 index a106d25..0000000 --- a/src/arcmira/entities/recommendations/types/list_recommendations_request_mention_class.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListRecommendationsRequestMentionClass = typing.Union[ - typing.Literal["ad_read", "endorsement", "mention", "all"], typing.Any -] diff --git a/src/arcmira/entities/types/__init__.py b/src/arcmira/entities/types/__init__.py index 7c6b7e8..2afc274 100644 --- a/src/arcmira/entities/types/__init__.py +++ b/src/arcmira/entities/types/__init__.py @@ -6,14 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .lookup_entities_request_type import LookupEntitiesRequestType from .resolve_entities_request_type import ResolveEntitiesRequestType - from .search_entities_request_type import SearchEntitiesRequestType -_dynamic_imports: typing.Dict[str, str] = { - "LookupEntitiesRequestType": ".lookup_entities_request_type", - "ResolveEntitiesRequestType": ".resolve_entities_request_type", - "SearchEntitiesRequestType": ".search_entities_request_type", -} +_dynamic_imports: typing.Dict[str, str] = {"ResolveEntitiesRequestType": ".resolve_entities_request_type"} def __getattr__(attr_name: str) -> typing.Any: @@ -37,4 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["LookupEntitiesRequestType", "ResolveEntitiesRequestType", "SearchEntitiesRequestType"] +__all__ = ["ResolveEntitiesRequestType"] diff --git a/src/arcmira/entities/types/lookup_entities_request_type.py b/src/arcmira/entities/types/lookup_entities_request_type.py deleted file mode 100644 index 9e27c9e..0000000 --- a/src/arcmira/entities/types/lookup_entities_request_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -LookupEntitiesRequestType = typing.Union[ - typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any -] diff --git a/src/arcmira/entities/types/search_entities_request_type.py b/src/arcmira/entities/types/search_entities_request_type.py deleted file mode 100644 index 8d3c8f9..0000000 --- a/src/arcmira/entities/types/search_entities_request_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SearchEntitiesRequestType = typing.Union[ - typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any -] diff --git a/src/arcmira/errors/__init__.py b/src/arcmira/errors/__init__.py index 1c9d8b3..babce4c 100644 --- a/src/arcmira/errors/__init__.py +++ b/src/arcmira/errors/__init__.py @@ -12,7 +12,6 @@ from .internal_server_error import InternalServerError from .not_found_error import NotFoundError from .payment_required_error import PaymentRequiredError - from .precondition_failed_error import PreconditionFailedError from .service_unavailable_error import ServiceUnavailableError from .too_many_requests_error import TooManyRequestsError from .unauthorized_error import UnauthorizedError @@ -23,7 +22,6 @@ "InternalServerError": ".internal_server_error", "NotFoundError": ".not_found_error", "PaymentRequiredError": ".payment_required_error", - "PreconditionFailedError": ".precondition_failed_error", "ServiceUnavailableError": ".service_unavailable_error", "TooManyRequestsError": ".too_many_requests_error", "UnauthorizedError": ".unauthorized_error", @@ -58,7 +56,6 @@ def __dir__(): "InternalServerError", "NotFoundError", "PaymentRequiredError", - "PreconditionFailedError", "ServiceUnavailableError", "TooManyRequestsError", "UnauthorizedError", diff --git a/src/arcmira/errors/precondition_failed_error.py b/src/arcmira/errors/precondition_failed_error.py deleted file mode 100644 index 0c41f0b..0000000 --- a/src/arcmira/errors/precondition_failed_error.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ..core.api_error import ApiError -from ..types.error import Error - - -class PreconditionFailedError(ApiError): - def __init__(self, body: Error, headers: typing.Optional[typing.Dict[str, str]] = None): - super().__init__(status_code=412, headers=headers, body=body) diff --git a/src/arcmira/feedback/__init__.py b/src/arcmira/feedback/__init__.py index a5427a8..7f160b3 100644 --- a/src/arcmira/feedback/__init__.py +++ b/src/arcmira/feedback/__init__.py @@ -7,18 +7,20 @@ if typing.TYPE_CHECKING: from .types import ( + SubmitFeedbackRequestCategory, SubmitFeedbackRequestCorrectionsItem, + SubmitFeedbackRequestCorrectionsItemClass, SubmitFeedbackRequestCorrectionsItemIssueType, - SubmitFeedbackRequestCorrectionsItemMentionClass, SubmitFeedbackRequestCorrectionsItemReason, SubmitFeedbackRequestCorrectionsItemSuggestedChange, SubmitFeedbackRequestMethod, SubmitFeedbackRequestType, ) _dynamic_imports: typing.Dict[str, str] = { + "SubmitFeedbackRequestCategory": ".types", "SubmitFeedbackRequestCorrectionsItem": ".types", + "SubmitFeedbackRequestCorrectionsItemClass": ".types", "SubmitFeedbackRequestCorrectionsItemIssueType": ".types", - "SubmitFeedbackRequestCorrectionsItemMentionClass": ".types", "SubmitFeedbackRequestCorrectionsItemReason": ".types", "SubmitFeedbackRequestCorrectionsItemSuggestedChange": ".types", "SubmitFeedbackRequestMethod": ".types", @@ -48,9 +50,10 @@ def __dir__(): __all__ = [ + "SubmitFeedbackRequestCategory", "SubmitFeedbackRequestCorrectionsItem", + "SubmitFeedbackRequestCorrectionsItemClass", "SubmitFeedbackRequestCorrectionsItemIssueType", - "SubmitFeedbackRequestCorrectionsItemMentionClass", "SubmitFeedbackRequestCorrectionsItemReason", "SubmitFeedbackRequestCorrectionsItemSuggestedChange", "SubmitFeedbackRequestMethod", diff --git a/src/arcmira/feedback/client.py b/src/arcmira/feedback/client.py index 5fa15c3..29068f2 100644 --- a/src/arcmira/feedback/client.py +++ b/src/arcmira/feedback/client.py @@ -7,6 +7,7 @@ from ..types.feedback_readback_response import FeedbackReadbackResponse from ..types.feedback_response import FeedbackResponse from .raw_client import AsyncRawFeedbackClient, RawFeedbackClient +from .types.submit_feedback_request_category import SubmitFeedbackRequestCategory from .types.submit_feedback_request_corrections_item import SubmitFeedbackRequestCorrectionsItem from .types.submit_feedback_request_method import SubmitFeedbackRequestMethod from .types.submit_feedback_request_type import SubmitFeedbackRequestType @@ -34,8 +35,8 @@ def submit( self, *, type: SubmitFeedbackRequestType, - query: typing.Dict[str, typing.Any], idempotency_key: typing.Optional[str] = None, + query: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, endpoint: typing.Optional[str] = OMIT, method: typing.Optional[SubmitFeedbackRequestMethod] = OMIT, request_id: typing.Optional[str] = OMIT, @@ -43,22 +44,24 @@ def submit( source_url: typing.Optional[str] = OMIT, notes: typing.Optional[str] = OMIT, corrections: typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] = OMIT, + category: typing.Optional[SubmitFeedbackRequestCategory] = OMIT, + mcp_call_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> FeedbackResponse: """ - Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. + Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query. Parameters ---------- type : SubmitFeedbackRequestType - The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search). - - query : typing.Dict[str, typing.Any] - The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional). idempotency_key : typing.Optional[str] 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. + query : typing.Optional[typing.Dict[str, typing.Any]] + The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + endpoint : typing.Optional[str] method : typing.Optional[SubmitFeedbackRequestMethod] @@ -70,8 +73,16 @@ def submit( source_url : typing.Optional[str] notes : typing.Optional[str] + Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing. corrections : typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] + Per-row corrections. Not allowed when type is experience. + + category : typing.Optional[SubmitFeedbackRequestCategory] + What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience. + + mcp_call_id : typing.Optional[str] + The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -91,13 +102,12 @@ def submit( client.feedback.submit( idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", type="recommendations", - query={"key": "value"}, ) """ _response = self._raw_client.submit( type=type, - query=query, idempotency_key=idempotency_key, + query=query, endpoint=endpoint, method=method, request_id=request_id, @@ -105,6 +115,8 @@ def submit( source_url=source_url, notes=notes, corrections=corrections, + category=category, + mcp_call_id=mcp_call_id, request_options=request_options, ) return _response.data @@ -118,7 +130,7 @@ def get( Parameters ---------- feedback_id : str - The feedback submission id POST /v1/feedback returned. + The feedback submission id POST /v1/feedback returned, fbk_ and digits. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -162,8 +174,8 @@ async def submit( self, *, type: SubmitFeedbackRequestType, - query: typing.Dict[str, typing.Any], idempotency_key: typing.Optional[str] = None, + query: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, endpoint: typing.Optional[str] = OMIT, method: typing.Optional[SubmitFeedbackRequestMethod] = OMIT, request_id: typing.Optional[str] = OMIT, @@ -171,22 +183,24 @@ async def submit( source_url: typing.Optional[str] = OMIT, notes: typing.Optional[str] = OMIT, corrections: typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] = OMIT, + category: typing.Optional[SubmitFeedbackRequestCategory] = OMIT, + mcp_call_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> FeedbackResponse: """ - Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. + Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query. Parameters ---------- type : SubmitFeedbackRequestType - The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search). - - query : typing.Dict[str, typing.Any] - The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional). idempotency_key : typing.Optional[str] 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. + query : typing.Optional[typing.Dict[str, typing.Any]] + The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + endpoint : typing.Optional[str] method : typing.Optional[SubmitFeedbackRequestMethod] @@ -198,8 +212,16 @@ async def submit( source_url : typing.Optional[str] notes : typing.Optional[str] + Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing. corrections : typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] + Per-row corrections. Not allowed when type is experience. + + category : typing.Optional[SubmitFeedbackRequestCategory] + What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience. + + mcp_call_id : typing.Optional[str] + The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -224,7 +246,6 @@ async def main() -> None: await client.feedback.submit( idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", type="recommendations", - query={"key": "value"}, ) @@ -232,8 +253,8 @@ async def main() -> None: """ _response = await self._raw_client.submit( type=type, - query=query, idempotency_key=idempotency_key, + query=query, endpoint=endpoint, method=method, request_id=request_id, @@ -241,6 +262,8 @@ async def main() -> None: source_url=source_url, notes=notes, corrections=corrections, + category=category, + mcp_call_id=mcp_call_id, request_options=request_options, ) return _response.data @@ -254,7 +277,7 @@ async def get( Parameters ---------- feedback_id : str - The feedback submission id POST /v1/feedback returned. + The feedback submission id POST /v1/feedback returned, fbk_ and digits. request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/src/arcmira/feedback/raw_client.py b/src/arcmira/feedback/raw_client.py index 6cbf5a7..3f100c7 100644 --- a/src/arcmira/feedback/raw_client.py +++ b/src/arcmira/feedback/raw_client.py @@ -21,6 +21,7 @@ from ..types.error import Error from ..types.feedback_readback_response import FeedbackReadbackResponse from ..types.feedback_response import FeedbackResponse +from .types.submit_feedback_request_category import SubmitFeedbackRequestCategory from .types.submit_feedback_request_corrections_item import SubmitFeedbackRequestCorrectionsItem from .types.submit_feedback_request_method import SubmitFeedbackRequestMethod from .types.submit_feedback_request_type import SubmitFeedbackRequestType @@ -38,8 +39,8 @@ def submit( self, *, type: SubmitFeedbackRequestType, - query: typing.Dict[str, typing.Any], idempotency_key: typing.Optional[str] = None, + query: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, endpoint: typing.Optional[str] = OMIT, method: typing.Optional[SubmitFeedbackRequestMethod] = OMIT, request_id: typing.Optional[str] = OMIT, @@ -47,22 +48,24 @@ def submit( source_url: typing.Optional[str] = OMIT, notes: typing.Optional[str] = OMIT, corrections: typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] = OMIT, + category: typing.Optional[SubmitFeedbackRequestCategory] = OMIT, + mcp_call_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[FeedbackResponse]: """ - Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. + Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query. Parameters ---------- type : SubmitFeedbackRequestType - The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search). - - query : typing.Dict[str, typing.Any] - The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional). idempotency_key : typing.Optional[str] 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. + query : typing.Optional[typing.Dict[str, typing.Any]] + The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + endpoint : typing.Optional[str] method : typing.Optional[SubmitFeedbackRequestMethod] @@ -74,8 +77,16 @@ def submit( source_url : typing.Optional[str] notes : typing.Optional[str] + Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing. corrections : typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] + Per-row corrections. Not allowed when type is experience. + + category : typing.Optional[SubmitFeedbackRequestCategory] + What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience. + + mcp_call_id : typing.Optional[str] + The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -102,6 +113,8 @@ def submit( annotation=typing.Sequence[SubmitFeedbackRequestCorrectionsItem], direction="write", ), + "category": category, + "mcp_call_id": mcp_call_id, }, headers={ "content-type": "application/json", @@ -215,7 +228,7 @@ def get( Parameters ---------- feedback_id : str - The feedback submission id POST /v1/feedback returned. + The feedback submission id POST /v1/feedback returned, fbk_ and digits. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -324,8 +337,8 @@ async def submit( self, *, type: SubmitFeedbackRequestType, - query: typing.Dict[str, typing.Any], idempotency_key: typing.Optional[str] = None, + query: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, endpoint: typing.Optional[str] = OMIT, method: typing.Optional[SubmitFeedbackRequestMethod] = OMIT, request_id: typing.Optional[str] = OMIT, @@ -333,22 +346,24 @@ async def submit( source_url: typing.Optional[str] = OMIT, notes: typing.Optional[str] = OMIT, corrections: typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] = OMIT, + category: typing.Optional[SubmitFeedbackRequestCategory] = OMIT, + mcp_call_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[FeedbackResponse]: """ - Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. + Attach corrections to the exact query you ran: pass the feedback type, the query object you sent, and optional per-item corrections. Public submissions are recorded for human review (status "logged"); nothing is auto-applied. Read the review status back later via GET /v1/feedback/{feedback_id}. recommendations and channel_sponsors feedback types require a Pro+ plan; every other type needs read. monitor_alert feedback targets fired alert rows: query carries monitor_id and/or tracker_id and/or alert_id, corrections target the alert row id, and every referenced alert row must belong to the caller (otherwise 404 alert_not_found). missed_alert corrections are expectations with no row to target: omit the correction id and put { source_url, approximate_timestamp_seconds?, entity_id? } in suggested_change. delivery_issue corrections may carry { channel } in suggested_change. experience feedback says how a task went as a whole rather than correcting a row: it requires category and notes, refuses corrections (400 invalid_feedback_request), and needs no query. category and mcp_call_id, when sent, are recorded in the stored query. Parameters ---------- type : SubmitFeedbackRequestType - The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/search hits), entities (/v1/entities/lookup and /v1/entities/{id} payloads), channels (/v1/channels/{slug} payloads), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows), search (rows from /v1/search or /v1/entities/search). - - query : typing.Dict[str, typing.Any] - The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + The surface being reviewed. Values: recommendations (/v1/recommendations rows by com_* id; requires a Pro+ plan), channel_sponsors (sponsor entities on a channel; requires a Pro+ plan), mentions (/v1/mentions rows by men_* id), entities_search (/v1/entities/resolve candidates), entities (/v1/entities/{id} payloads), channels (/v1/channels/{channel_id}/videos rows), monitor_alert (fired alert rows from /v1/monitors/{id}/alerts or /v1/trackers/{id}/alerts; corrections target the alert row id), appearances (person appearance rows from /v1/mentions with is_appearance=true), search (/v1/search passages), experience (how a task went as a whole, not one row: requires category and notes, takes no corrections, and query is optional). idempotency_key : typing.Optional[str] 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. + query : typing.Optional[typing.Dict[str, typing.Any]] + The query object that produced the result you are reviewing, echoed back verbatim so reviewers can replay it. For monitor_alert feedback, carry monitor_id and/or tracker_id and/or alert_id. + endpoint : typing.Optional[str] method : typing.Optional[SubmitFeedbackRequestMethod] @@ -360,8 +375,16 @@ async def submit( source_url : typing.Optional[str] notes : typing.Optional[str] + Free text for the reviewer. Required when type is experience: say what the user asked for and what went wrong, slow, or missing. corrections : typing.Optional[typing.Sequence[SubmitFeedbackRequestCorrectionsItem]] + Per-row corrections. Not allowed when type is experience. + + category : typing.Optional[SubmitFeedbackRequestCategory] + What kind of problem this is. Values: wrong_entity (a name resolved to the wrong person, company or thing), bad_data (a row or field is wrong), missing (something that should exist was not found), slow (the task took too long), confusing (the answer or an error was hard to act on), other (anything else; say what in notes). Required when type is experience. + + mcp_call_id : typing.Optional[str] + The MCP tool call this feedback is about, as the Arcmira MCP server names it (mcpc_ and 32 hex digits). Joins the feedback to that call in product analytics. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -388,6 +411,8 @@ async def submit( annotation=typing.Sequence[SubmitFeedbackRequestCorrectionsItem], direction="write", ), + "category": category, + "mcp_call_id": mcp_call_id, }, headers={ "content-type": "application/json", @@ -501,7 +526,7 @@ async def get( Parameters ---------- feedback_id : str - The feedback submission id POST /v1/feedback returned. + The feedback submission id POST /v1/feedback returned, fbk_ and digits. request_options : typing.Optional[RequestOptions] Request-specific configuration. diff --git a/src/arcmira/feedback/types/__init__.py b/src/arcmira/feedback/types/__init__.py index 9f745e3..2aea23f 100644 --- a/src/arcmira/feedback/types/__init__.py +++ b/src/arcmira/feedback/types/__init__.py @@ -6,9 +6,10 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .submit_feedback_request_category import SubmitFeedbackRequestCategory from .submit_feedback_request_corrections_item import SubmitFeedbackRequestCorrectionsItem + from .submit_feedback_request_corrections_item_class import SubmitFeedbackRequestCorrectionsItemClass from .submit_feedback_request_corrections_item_issue_type import SubmitFeedbackRequestCorrectionsItemIssueType - from .submit_feedback_request_corrections_item_mention_class import SubmitFeedbackRequestCorrectionsItemMentionClass from .submit_feedback_request_corrections_item_reason import SubmitFeedbackRequestCorrectionsItemReason from .submit_feedback_request_corrections_item_suggested_change import ( SubmitFeedbackRequestCorrectionsItemSuggestedChange, @@ -16,9 +17,10 @@ from .submit_feedback_request_method import SubmitFeedbackRequestMethod from .submit_feedback_request_type import SubmitFeedbackRequestType _dynamic_imports: typing.Dict[str, str] = { + "SubmitFeedbackRequestCategory": ".submit_feedback_request_category", "SubmitFeedbackRequestCorrectionsItem": ".submit_feedback_request_corrections_item", + "SubmitFeedbackRequestCorrectionsItemClass": ".submit_feedback_request_corrections_item_class", "SubmitFeedbackRequestCorrectionsItemIssueType": ".submit_feedback_request_corrections_item_issue_type", - "SubmitFeedbackRequestCorrectionsItemMentionClass": ".submit_feedback_request_corrections_item_mention_class", "SubmitFeedbackRequestCorrectionsItemReason": ".submit_feedback_request_corrections_item_reason", "SubmitFeedbackRequestCorrectionsItemSuggestedChange": ".submit_feedback_request_corrections_item_suggested_change", "SubmitFeedbackRequestMethod": ".submit_feedback_request_method", @@ -48,9 +50,10 @@ def __dir__(): __all__ = [ + "SubmitFeedbackRequestCategory", "SubmitFeedbackRequestCorrectionsItem", + "SubmitFeedbackRequestCorrectionsItemClass", "SubmitFeedbackRequestCorrectionsItemIssueType", - "SubmitFeedbackRequestCorrectionsItemMentionClass", "SubmitFeedbackRequestCorrectionsItemReason", "SubmitFeedbackRequestCorrectionsItemSuggestedChange", "SubmitFeedbackRequestMethod", diff --git a/src/arcmira/feedback/types/submit_feedback_request_category.py b/src/arcmira/feedback/types/submit_feedback_request_category.py new file mode 100644 index 0000000..5c11400 --- /dev/null +++ b/src/arcmira/feedback/types/submit_feedback_request_category.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubmitFeedbackRequestCategory = typing.Union[ + typing.Literal["wrong_entity", "bad_data", "missing", "slow", "confusing", "other"], typing.Any +] diff --git a/src/arcmira/feedback/types/submit_feedback_request_corrections_item.py b/src/arcmira/feedback/types/submit_feedback_request_corrections_item.py index 9c9254e..dcca580 100644 --- a/src/arcmira/feedback/types/submit_feedback_request_corrections_item.py +++ b/src/arcmira/feedback/types/submit_feedback_request_corrections_item.py @@ -3,9 +3,11 @@ import typing import pydantic +import typing_extensions from ...core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ...core.serialization import FieldMetadata +from .submit_feedback_request_corrections_item_class import SubmitFeedbackRequestCorrectionsItemClass from .submit_feedback_request_corrections_item_issue_type import SubmitFeedbackRequestCorrectionsItemIssueType -from .submit_feedback_request_corrections_item_mention_class import SubmitFeedbackRequestCorrectionsItemMentionClass from .submit_feedback_request_corrections_item_reason import SubmitFeedbackRequestCorrectionsItemReason from .submit_feedback_request_corrections_item_suggested_change import ( SubmitFeedbackRequestCorrectionsItemSuggestedChange, @@ -18,7 +20,18 @@ class SubmitFeedbackRequestCorrectionsItem(UniversalBaseModel): Public id of the row being corrected, from the response you received: men_* (mentions, appearances), com_* (recommendations), ent_* (entities, sponsors), or the alert row id (monitor_alert). Omit for missed_alert and missing_result corrections, which have no row to target. """ - mention_class: typing.Optional[SubmitFeedbackRequestCorrectionsItemMentionClass] = None + class_: typing_extensions.Annotated[ + typing.Optional[SubmitFeedbackRequestCorrectionsItemClass], + FieldMetadata(alias="class"), + pydantic.Field( + alias="class", + description="On recommendations feedback, the class the row should carry. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).", + ), + ] = None + """ + On recommendations feedback, the class the row should carry. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). + """ + reason: typing.Optional[SubmitFeedbackRequestCorrectionsItemReason] = None issue_type: typing.Optional[SubmitFeedbackRequestCorrectionsItemIssueType] = pydantic.Field(default=None) """ diff --git a/src/arcmira/feedback/types/submit_feedback_request_corrections_item_class.py b/src/arcmira/feedback/types/submit_feedback_request_corrections_item_class.py new file mode 100644 index 0000000..c530b89 --- /dev/null +++ b/src/arcmira/feedback/types/submit_feedback_request_corrections_item_class.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +SubmitFeedbackRequestCorrectionsItemClass = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/feedback/types/submit_feedback_request_corrections_item_mention_class.py b/src/arcmira/feedback/types/submit_feedback_request_corrections_item_mention_class.py deleted file mode 100644 index 977460b..0000000 --- a/src/arcmira/feedback/types/submit_feedback_request_corrections_item_mention_class.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SubmitFeedbackRequestCorrectionsItemMentionClass = typing.Union[ - typing.Literal["ad_read", "endorsement", "mention"], typing.Any -] diff --git a/src/arcmira/feedback/types/submit_feedback_request_type.py b/src/arcmira/feedback/types/submit_feedback_request_type.py index 1ae5933..22218d8 100644 --- a/src/arcmira/feedback/types/submit_feedback_request_type.py +++ b/src/arcmira/feedback/types/submit_feedback_request_type.py @@ -13,6 +13,7 @@ "monitor_alert", "appearances", "search", + "experience", ], typing.Any, ] diff --git a/src/arcmira/team/__init__.py b/src/arcmira/integrations/__init__.py similarity index 87% rename from src/arcmira/team/__init__.py rename to src/arcmira/integrations/__init__.py index 44c445d..bd92dcf 100644 --- a/src/arcmira/team/__init__.py +++ b/src/arcmira/integrations/__init__.py @@ -6,8 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from . import usage_events -_dynamic_imports: typing.Dict[str, str] = {"usage_events": ".usage_events"} + from . import slack +_dynamic_imports: typing.Dict[str, str] = {"slack": ".slack"} def __getattr__(attr_name: str) -> typing.Any: @@ -31,4 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["usage_events"] +__all__ = ["slack"] diff --git a/src/arcmira/integrations/client.py b/src/arcmira/integrations/client.py new file mode 100644 index 0000000..5d8c674 --- /dev/null +++ b/src/arcmira/integrations/client.py @@ -0,0 +1,63 @@ +# This file was auto-generated by Fern from our API Definition. + +from __future__ import annotations + +import typing + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from .raw_client import AsyncRawIntegrationsClient, RawIntegrationsClient + +if typing.TYPE_CHECKING: + from .slack.client import AsyncSlackClient, SlackClient + + +class IntegrationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawIntegrationsClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._slack: typing.Optional[SlackClient] = None + + @property + def with_raw_response(self) -> RawIntegrationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawIntegrationsClient + """ + return self._raw_client + + @property + def slack(self): + if self._slack is None: + from .slack.client import SlackClient # noqa: E402 + + self._slack = SlackClient(client_wrapper=self._client_wrapper) + return self._slack + + +class AsyncIntegrationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawIntegrationsClient(client_wrapper=client_wrapper) + self._client_wrapper = client_wrapper + self._slack: typing.Optional[AsyncSlackClient] = None + + @property + def with_raw_response(self) -> AsyncRawIntegrationsClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawIntegrationsClient + """ + return self._raw_client + + @property + def slack(self): + if self._slack is None: + from .slack.client import AsyncSlackClient # noqa: E402 + + self._slack = AsyncSlackClient(client_wrapper=self._client_wrapper) + return self._slack diff --git a/src/arcmira/integrations/raw_client.py b/src/arcmira/integrations/raw_client.py new file mode 100644 index 0000000..b917bc5 --- /dev/null +++ b/src/arcmira/integrations/raw_client.py @@ -0,0 +1,13 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper + + +class RawIntegrationsClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._client_wrapper = client_wrapper + + +class AsyncRawIntegrationsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper diff --git a/src/arcmira/team/usage_events/__init__.py b/src/arcmira/integrations/slack/__init__.py similarity index 100% rename from src/arcmira/team/usage_events/__init__.py rename to src/arcmira/integrations/slack/__init__.py diff --git a/src/arcmira/integrations/slack/client.py b/src/arcmira/integrations/slack/client.py new file mode 100644 index 0000000..7221f10 --- /dev/null +++ b/src/arcmira/integrations/slack/client.py @@ -0,0 +1,100 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.slack_integration_list_response import SlackIntegrationListResponse +from .raw_client import AsyncRawSlackClient, RawSlackClient + + +class SlackClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawSlackClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawSlackClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawSlackClient + """ + return self._raw_client + + def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> SlackIntegrationListResponse: + """ + The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SlackIntegrationListResponse + Success + + Examples + -------- + from arcmira import Arcmira + + client = Arcmira( + api_key="YOUR_API_KEY", + ) + client.integrations.slack.list() + """ + _response = self._raw_client.list(request_options=request_options) + return _response.data + + +class AsyncSlackClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawSlackClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawSlackClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawSlackClient + """ + return self._raw_client + + async def list(self, *, request_options: typing.Optional[RequestOptions] = None) -> SlackIntegrationListResponse: + """ + The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination. + + Parameters + ---------- + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + SlackIntegrationListResponse + Success + + Examples + -------- + import asyncio + + from arcmira import AsyncArcmira + + client = AsyncArcmira( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.integrations.slack.list() + + + asyncio.run(main()) + """ + _response = await self._raw_client.list(request_options=request_options) + return _response.data diff --git a/src/arcmira/topics/raw_client.py b/src/arcmira/integrations/slack/raw_client.py similarity index 74% rename from src/arcmira/topics/raw_client.py rename to src/arcmira/integrations/slack/raw_client.py index 6416ad4..c5d9011 100644 --- a/src/arcmira/topics/raw_client.py +++ b/src/arcmira/integrations/slack/raw_client.py @@ -3,57 +3,54 @@ import typing from json.decoder import JSONDecodeError -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import encode_path_param -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.payment_required_error import PaymentRequiredError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.error import Error -from ..types.topic_page_response import TopicPageResponse +from ...core.api_error import ApiError +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse +from ...core.parse_error import ParsingError +from ...core.pydantic_utilities import parse_obj_as +from ...core.request_options import RequestOptions +from ...errors.bad_request_error import BadRequestError +from ...errors.forbidden_error import ForbiddenError +from ...errors.internal_server_error import InternalServerError +from ...errors.not_found_error import NotFoundError +from ...errors.too_many_requests_error import TooManyRequestsError +from ...errors.unauthorized_error import UnauthorizedError +from ...types.error import Error +from ...types.slack_integration_list_response import SlackIntegrationListResponse from pydantic import ValidationError -class RawTopicsClient: +class RawSlackClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[TopicPageResponse]: + def list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> HttpResponse[SlackIntegrationListResponse]: """ + The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination. + Parameters ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[TopicPageResponse] + HttpResponse[SlackIntegrationListResponse] Success """ _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}", + "v1/integrations/slack", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - TopicPageResponse, + SlackIntegrationListResponse, parse_obj_as( - type_=TopicPageResponse, # type: ignore + type_=SlackIntegrationListResponse, # type: ignore object_=_response.json(), ), ) @@ -80,17 +77,6 @@ def get( ), ), ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) if _response.status_code == 403: raise ForbiddenError( headers=dict(_response.headers), @@ -145,38 +131,37 @@ def get( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) -class AsyncRawTopicsClient: +class AsyncRawSlackClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[TopicPageResponse]: + async def list( + self, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[SlackIntegrationListResponse]: """ + The account's active Slack workspaces with the ids a monitor needs for Slack delivery: create or PATCH a monitor with notify_slack: true, slack_integration_id set to an id here, and optionally slack_channel_id (default_channel_id when omitted). Slack is connected in the dashboard, never through the API; an empty list means the user must connect it there first. Reads stored data only. Single page, no pagination. + Parameters ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncHttpResponse[TopicPageResponse] + AsyncHttpResponse[SlackIntegrationListResponse] Success """ _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}", + "v1/integrations/slack", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - TopicPageResponse, + SlackIntegrationListResponse, parse_obj_as( - type_=TopicPageResponse, # type: ignore + type_=SlackIntegrationListResponse, # type: ignore object_=_response.json(), ), ) @@ -203,17 +188,6 @@ async def get( ), ), ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) if _response.status_code == 403: raise ForbiddenError( headers=dict(_response.headers), diff --git a/src/arcmira/mentions/__init__.py b/src/arcmira/mentions/__init__.py index 2c840a8..652db0c 100644 --- a/src/arcmira/mentions/__init__.py +++ b/src/arcmira/mentions/__init__.py @@ -6,16 +6,10 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ( - CountMentionsRequestMode, - ListMentionsRequestDetails, - ListMentionsRequestEntityType, - ListMentionsRequestSentiment, - ) + from .types import CountMentionsRequestMode, ListMentionsRequestDetails, ListMentionsRequestSentiment _dynamic_imports: typing.Dict[str, str] = { "CountMentionsRequestMode": ".types", "ListMentionsRequestDetails": ".types", - "ListMentionsRequestEntityType": ".types", "ListMentionsRequestSentiment": ".types", } @@ -41,9 +35,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = [ - "CountMentionsRequestMode", - "ListMentionsRequestDetails", - "ListMentionsRequestEntityType", - "ListMentionsRequestSentiment", -] +__all__ = ["CountMentionsRequestMode", "ListMentionsRequestDetails", "ListMentionsRequestSentiment"] diff --git a/src/arcmira/mentions/client.py b/src/arcmira/mentions/client.py index 221d1bb..224f23d 100644 --- a/src/arcmira/mentions/client.py +++ b/src/arcmira/mentions/client.py @@ -11,7 +11,6 @@ from .raw_client import AsyncRawMentionsClient, RawMentionsClient from .types.count_mentions_request_mode import CountMentionsRequestMode from .types.list_mentions_request_details import ListMentionsRequestDetails -from .types.list_mentions_request_entity_type import ListMentionsRequestEntityType from .types.list_mentions_request_sentiment import ListMentionsRequestSentiment @@ -33,40 +32,33 @@ def with_raw_response(self) -> RawMentionsClient: def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListMentionsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, q: typing.Optional[str] = None, sentiment: typing.Optional[ListMentionsRequestSentiment] = None, is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, details: typing.Optional[ListMentionsRequestDetails] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[Mention, MentionListResponse]: """ - Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListMentionsRequestEntityType] - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. q : typing.Optional[str] @@ -74,9 +66,11 @@ def list( is_appearance : typing.Optional[bool] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. details : typing.Optional[ListMentionsRequestDetails] @@ -95,7 +89,9 @@ def list( client = Arcmira( api_key="YOUR_API_KEY", ) - response = client.mentions.list() + response = client.mentions.list( + entity_id="entity_id", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -103,18 +99,15 @@ def list( yield page """ return self._raw_client.list( + entity_id=entity_id, limit=limit, cursor=cursor, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, q=q, sentiment=sentiment, is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, + after=after, + before=before, details=details, request_options=request_options, ) @@ -127,13 +120,13 @@ def count( video_ids: typing.Optional[str] = None, entity_types: typing.Optional[str] = None, mode: typing.Optional[CountMentionsRequestMode] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> MentionCountsResponse: """ - A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -152,11 +145,11 @@ def count( mode : typing.Optional[CountMentionsRequestMode] mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions. - published_after : typing.Optional[str] - ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. limit : typing.Optional[int] Rows in the ranked table, 1 to 40. Default 20. @@ -184,8 +177,8 @@ def count( video_ids=video_ids, entity_types=entity_types, mode=mode, - published_after=published_after, - published_before=published_before, + after=after, + before=before, limit=limit, request_options=request_options, ) @@ -210,40 +203,33 @@ def with_raw_response(self) -> AsyncRawMentionsClient: async def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListMentionsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, q: typing.Optional[str] = None, sentiment: typing.Optional[ListMentionsRequestSentiment] = None, is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, details: typing.Optional[ListMentionsRequestDetails] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[Mention, MentionListResponse]: """ - Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListMentionsRequestEntityType] - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. q : typing.Optional[str] @@ -251,9 +237,11 @@ async def list( is_appearance : typing.Optional[bool] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. details : typing.Optional[ListMentionsRequestDetails] @@ -277,7 +265,9 @@ async def list( async def main() -> None: - response = await client.mentions.list() + response = await client.mentions.list( + entity_id="entity_id", + ) async for item in response: yield item @@ -289,18 +279,15 @@ async def main() -> None: asyncio.run(main()) """ return await self._raw_client.list( + entity_id=entity_id, limit=limit, cursor=cursor, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, q=q, sentiment=sentiment, is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, + after=after, + before=before, details=details, request_options=request_options, ) @@ -313,13 +300,13 @@ async def count( video_ids: typing.Optional[str] = None, entity_types: typing.Optional[str] = None, mode: typing.Optional[CountMentionsRequestMode] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> MentionCountsResponse: """ - A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -338,11 +325,11 @@ async def count( mode : typing.Optional[CountMentionsRequestMode] mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions. - published_after : typing.Optional[str] - ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. limit : typing.Optional[int] Rows in the ranked table, 1 to 40. Default 20. @@ -378,8 +365,8 @@ async def main() -> None: video_ids=video_ids, entity_types=entity_types, mode=mode, - published_after=published_after, - published_before=published_before, + after=after, + before=before, limit=limit, request_options=request_options, ) diff --git a/src/arcmira/mentions/raw_client.py b/src/arcmira/mentions/raw_client.py index f90dd51..b44a564 100644 --- a/src/arcmira/mentions/raw_client.py +++ b/src/arcmira/mentions/raw_client.py @@ -23,7 +23,6 @@ from ..types.mention_list_response import MentionListResponse from .types.count_mentions_request_mode import CountMentionsRequestMode from .types.list_mentions_request_details import ListMentionsRequestDetails -from .types.list_mentions_request_entity_type import ListMentionsRequestEntityType from .types.list_mentions_request_sentiment import ListMentionsRequestSentiment from pydantic import ValidationError @@ -35,40 +34,33 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListMentionsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, q: typing.Optional[str] = None, sentiment: typing.Optional[ListMentionsRequestSentiment] = None, is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, details: typing.Optional[ListMentionsRequestDetails] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[Mention, MentionListResponse]: """ - Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListMentionsRequestEntityType] - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. q : typing.Optional[str] @@ -76,9 +68,11 @@ def list( is_appearance : typing.Optional[bool] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. details : typing.Optional[ListMentionsRequestDetails] @@ -97,15 +91,12 @@ def list( "limit": limit, "cursor": cursor, "entity_id": entity_id, - "entity_name": entity_name, - "entity_type": entity_type, "channel_id": channel_id, - "channel_name": channel_name, "q": q, "sentiment": sentiment, "is_appearance": is_appearance, - "date_from": date_from, - "date_to": date_to, + "after": after, + "before": before, "details": details, }, request_options=request_options, @@ -119,22 +110,19 @@ def list( object_=_response.json(), ), ) - _items = _parsed_response.data + _items = _parsed_response.mentions _parsed_next = _parsed_response.next_cursor _has_next = _parsed_next is not None and _parsed_next != "" _get_next = lambda: self.list( + entity_id=entity_id, limit=limit, cursor=_parsed_next, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, q=q, sentiment=sentiment, is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, + after=after, + before=before, details=details, request_options=request_options, ) @@ -233,13 +221,13 @@ def count( video_ids: typing.Optional[str] = None, entity_types: typing.Optional[str] = None, mode: typing.Optional[CountMentionsRequestMode] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MentionCountsResponse]: """ - A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -258,11 +246,11 @@ def count( mode : typing.Optional[CountMentionsRequestMode] mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions. - published_after : typing.Optional[str] - ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. limit : typing.Optional[int] Rows in the ranked table, 1 to 40. Default 20. @@ -284,8 +272,8 @@ def count( "video_ids": video_ids, "entity_types": entity_types, "mode": mode, - "published_after": published_after, - "published_before": published_before, + "after": after, + "before": before, "limit": limit, }, request_options=request_options, @@ -394,40 +382,33 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListMentionsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, q: typing.Optional[str] = None, sentiment: typing.Optional[ListMentionsRequestSentiment] = None, is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, details: typing.Optional[ListMentionsRequestDetails] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[Mention, MentionListResponse]: """ - Cursor-paginated mentions filtered by entity (entity_id or entity_name is required), channel, text query, sentiment, appearance flag, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Cursor-paginated mentions filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), text query, sentiment, appearance flag, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Positions are start_seconds and end_seconds (integer seconds; 0 means full episode). is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListMentionsRequestEntityType] - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. q : typing.Optional[str] @@ -435,9 +416,11 @@ async def list( is_appearance : typing.Optional[bool] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. details : typing.Optional[ListMentionsRequestDetails] @@ -456,15 +439,12 @@ async def list( "limit": limit, "cursor": cursor, "entity_id": entity_id, - "entity_name": entity_name, - "entity_type": entity_type, "channel_id": channel_id, - "channel_name": channel_name, "q": q, "sentiment": sentiment, "is_appearance": is_appearance, - "date_from": date_from, - "date_to": date_to, + "after": after, + "before": before, "details": details, }, request_options=request_options, @@ -478,24 +458,21 @@ async def list( object_=_response.json(), ), ) - _items = _parsed_response.data + _items = _parsed_response.mentions _parsed_next = _parsed_response.next_cursor _has_next = _parsed_next is not None and _parsed_next != "" async def _get_next(): return await self.list( + entity_id=entity_id, limit=limit, cursor=_parsed_next, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, q=q, sentiment=sentiment, is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, + after=after, + before=before, details=details, request_options=request_options, ) @@ -595,13 +572,13 @@ async def count( video_ids: typing.Optional[str] = None, entity_types: typing.Optional[str] = None, mode: typing.Optional[CountMentionsRequestMode] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MentionCountsResponse]: """ - A small ranked table of entity and channel counts, all-time unless published_after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + A small ranked table of entity and channel counts, all-time unless after is set. Pass channel_ids for what shows talk about and entity_types to match the question (topic for subjects, person for guests, organization,product for brands). Pass video_ids with one id from GET /v1/channels/{channel_id}/videos for what a single episode mentions. Two or more channel_ids also return shared, the entities on more than one of them ranked by the smallest per-channel count, which is true overlap. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per table row returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -620,11 +597,11 @@ async def count( mode : typing.Optional[CountMentionsRequestMode] mentions counts talk about an entity; appearances counts a person being present; both counts either. Default mentions. - published_after : typing.Optional[str] - ISO date. Counts are all-time without it. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. Counts are all-time without it. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. limit : typing.Optional[int] Rows in the ranked table, 1 to 40. Default 20. @@ -646,8 +623,8 @@ async def count( "video_ids": video_ids, "entity_types": entity_types, "mode": mode, - "published_after": published_after, - "published_before": published_before, + "after": after, + "before": before, "limit": limit, }, request_options=request_options, diff --git a/src/arcmira/mentions/types/__init__.py b/src/arcmira/mentions/types/__init__.py index 710d8c6..f22b307 100644 --- a/src/arcmira/mentions/types/__init__.py +++ b/src/arcmira/mentions/types/__init__.py @@ -8,12 +8,10 @@ if typing.TYPE_CHECKING: from .count_mentions_request_mode import CountMentionsRequestMode from .list_mentions_request_details import ListMentionsRequestDetails - from .list_mentions_request_entity_type import ListMentionsRequestEntityType from .list_mentions_request_sentiment import ListMentionsRequestSentiment _dynamic_imports: typing.Dict[str, str] = { "CountMentionsRequestMode": ".count_mentions_request_mode", "ListMentionsRequestDetails": ".list_mentions_request_details", - "ListMentionsRequestEntityType": ".list_mentions_request_entity_type", "ListMentionsRequestSentiment": ".list_mentions_request_sentiment", } @@ -39,9 +37,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = [ - "CountMentionsRequestMode", - "ListMentionsRequestDetails", - "ListMentionsRequestEntityType", - "ListMentionsRequestSentiment", -] +__all__ = ["CountMentionsRequestMode", "ListMentionsRequestDetails", "ListMentionsRequestSentiment"] diff --git a/src/arcmira/mentions/types/list_mentions_request_entity_type.py b/src/arcmira/mentions/types/list_mentions_request_entity_type.py deleted file mode 100644 index d171eb8..0000000 --- a/src/arcmira/mentions/types/list_mentions_request_entity_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListMentionsRequestEntityType = typing.Union[ - typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any -] diff --git a/src/arcmira/monitors/__init__.py b/src/arcmira/monitors/__init__.py index 061b697..f3a8782 100644 --- a/src/arcmira/monitors/__init__.py +++ b/src/arcmira/monitors/__init__.py @@ -7,11 +7,14 @@ if typing.TYPE_CHECKING: from .types import CreateMonitorsRequestNotifyFrequency, UpdateMonitorsRequestNotifyFrequency - from . import alerts, trackers + from . import alerts, entities, trackers + from .entities import AddEntitiesRequestPersonMatchMode _dynamic_imports: typing.Dict[str, str] = { + "AddEntitiesRequestPersonMatchMode": ".entities", "CreateMonitorsRequestNotifyFrequency": ".types", "UpdateMonitorsRequestNotifyFrequency": ".types", "alerts": ".alerts", + "entities": ".entities", "trackers": ".trackers", } @@ -37,4 +40,11 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["CreateMonitorsRequestNotifyFrequency", "UpdateMonitorsRequestNotifyFrequency", "alerts", "trackers"] +__all__ = [ + "AddEntitiesRequestPersonMatchMode", + "CreateMonitorsRequestNotifyFrequency", + "UpdateMonitorsRequestNotifyFrequency", + "alerts", + "entities", + "trackers", +] diff --git a/src/arcmira/monitors/client.py b/src/arcmira/monitors/client.py index 9d76dcd..37a962e 100644 --- a/src/arcmira/monitors/client.py +++ b/src/arcmira/monitors/client.py @@ -16,6 +16,7 @@ if typing.TYPE_CHECKING: from .alerts.client import AlertsClient, AsyncAlertsClient + from .entities.client import AsyncEntitiesClient, EntitiesClient from .trackers.client import AsyncTrackersClient, TrackersClient # this is used as the default value for optional parameters OMIT = typing.cast(typing.Any, ...) @@ -27,6 +28,7 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper self._trackers: typing.Optional[TrackersClient] = None self._alerts: typing.Optional[AlertsClient] = None + self._entities: typing.Optional[EntitiesClient] = None @property def with_raw_response(self) -> RawMonitorsClient: @@ -79,10 +81,11 @@ def create( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, + team_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorMutationResponse: """ - Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint. + Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint. Parameters ---------- @@ -105,7 +108,7 @@ def create( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -119,6 +122,9 @@ def create( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. + team_id : typing.Optional[str] + Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -151,6 +157,7 @@ def create( notify_slack=notify_slack, slack_integration_id=slack_integration_id, slack_channel_id=slack_channel_id, + team_id=team_id, request_options=request_options, ) return _response.data @@ -163,7 +170,7 @@ def delete( request_options: typing.Optional[RequestOptions] = None, ) -> MonitorDeleteResponse: """ - Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. + Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. Parameters ---------- @@ -211,13 +218,11 @@ def update( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, - is_paused: typing.Optional[bool] = OMIT, - is_collapsed: typing.Optional[bool] = OMIT, - sort_order: typing.Optional[int] = OMIT, + paused: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorMutationResponse: """ - A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. + A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. Parameters ---------- @@ -243,7 +248,7 @@ def update( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -257,15 +262,9 @@ def update( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. - is_paused : typing.Optional[bool] + paused : typing.Optional[bool] Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued. - is_collapsed : typing.Optional[bool] - Dashboard display state. - - sort_order : typing.Optional[int] - Dashboard sort position. - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -299,9 +298,7 @@ def update( notify_slack=notify_slack, slack_integration_id=slack_integration_id, slack_channel_id=slack_channel_id, - is_paused=is_paused, - is_collapsed=is_collapsed, - sort_order=sort_order, + paused=paused, request_options=request_options, ) return _response.data @@ -314,7 +311,7 @@ def rotate_webhook_secret( request_options: typing.Optional[RequestOptions] = None, ) -> WebhookSecretRotateResponse: """ - Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope. + Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope. Parameters ---------- @@ -365,6 +362,14 @@ def alerts(self): self._alerts = AlertsClient(client_wrapper=self._client_wrapper) return self._alerts + @property + def entities(self): + if self._entities is None: + from .entities.client import EntitiesClient # noqa: E402 + + self._entities = EntitiesClient(client_wrapper=self._client_wrapper) + return self._entities + class AsyncMonitorsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -372,6 +377,7 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper self._trackers: typing.Optional[AsyncTrackersClient] = None self._alerts: typing.Optional[AsyncAlertsClient] = None + self._entities: typing.Optional[AsyncEntitiesClient] = None @property def with_raw_response(self) -> AsyncRawMonitorsClient: @@ -432,10 +438,11 @@ async def create( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, + team_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorMutationResponse: """ - Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint. + Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint. Parameters ---------- @@ -458,7 +465,7 @@ async def create( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -472,6 +479,9 @@ async def create( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. + team_id : typing.Optional[str] + Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -512,6 +522,7 @@ async def main() -> None: notify_slack=notify_slack, slack_integration_id=slack_integration_id, slack_channel_id=slack_channel_id, + team_id=team_id, request_options=request_options, ) return _response.data @@ -524,7 +535,7 @@ async def delete( request_options: typing.Optional[RequestOptions] = None, ) -> MonitorDeleteResponse: """ - Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. + Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. Parameters ---------- @@ -580,13 +591,11 @@ async def update( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, - is_paused: typing.Optional[bool] = OMIT, - is_collapsed: typing.Optional[bool] = OMIT, - sort_order: typing.Optional[int] = OMIT, + paused: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorMutationResponse: """ - A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. + A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. Parameters ---------- @@ -612,7 +621,7 @@ async def update( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -626,15 +635,9 @@ async def update( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. - is_paused : typing.Optional[bool] + paused : typing.Optional[bool] Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued. - is_collapsed : typing.Optional[bool] - Dashboard display state. - - sort_order : typing.Optional[int] - Dashboard sort position. - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -676,9 +679,7 @@ async def main() -> None: notify_slack=notify_slack, slack_integration_id=slack_integration_id, slack_channel_id=slack_channel_id, - is_paused=is_paused, - is_collapsed=is_collapsed, - sort_order=sort_order, + paused=paused, request_options=request_options, ) return _response.data @@ -691,7 +692,7 @@ async def rotate_webhook_secret( request_options: typing.Optional[RequestOptions] = None, ) -> WebhookSecretRotateResponse: """ - Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope. + Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope. Parameters ---------- @@ -749,3 +750,11 @@ def alerts(self): self._alerts = AsyncAlertsClient(client_wrapper=self._client_wrapper) return self._alerts + + @property + def entities(self): + if self._entities is None: + from .entities.client import AsyncEntitiesClient # noqa: E402 + + self._entities = AsyncEntitiesClient(client_wrapper=self._client_wrapper) + return self._entities diff --git a/src/arcmira/entities/recommendations/__init__.py b/src/arcmira/monitors/entities/__init__.py similarity index 81% rename from src/arcmira/entities/recommendations/__init__.py rename to src/arcmira/monitors/entities/__init__.py index e888766..7b457fc 100644 --- a/src/arcmira/entities/recommendations/__init__.py +++ b/src/arcmira/monitors/entities/__init__.py @@ -6,8 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ListRecommendationsRequestMentionClass -_dynamic_imports: typing.Dict[str, str] = {"ListRecommendationsRequestMentionClass": ".types"} + from .types import AddEntitiesRequestPersonMatchMode +_dynamic_imports: typing.Dict[str, str] = {"AddEntitiesRequestPersonMatchMode": ".types"} def __getattr__(attr_name: str) -> typing.Any: @@ -31,4 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ListRecommendationsRequestMentionClass"] +__all__ = ["AddEntitiesRequestPersonMatchMode"] diff --git a/src/arcmira/monitors/entities/client.py b/src/arcmira/monitors/entities/client.py new file mode 100644 index 0000000..bce4cef --- /dev/null +++ b/src/arcmira/monitors/entities/client.py @@ -0,0 +1,164 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.request_options import RequestOptions +from ...types.monitor_add_entities_response import MonitorAddEntitiesResponse +from .raw_client import AsyncRawEntitiesClient, RawEntitiesClient +from .types.add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode + +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) + + +class EntitiesClient: + def __init__(self, *, client_wrapper: SyncClientWrapper): + self._raw_client = RawEntitiesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> RawEntitiesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + RawEntitiesClient + """ + return self._raw_client + + def add( + self, + id: str, + *, + entity_ids: typing.Sequence[str], + idempotency_key: typing.Optional[str] = None, + person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> MonitorAddEntitiesResponse: + """ + Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + + Parameters + ---------- + id : str + Monitor id. + + entity_ids : typing.Sequence[str] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. + + idempotency_key : typing.Optional[str] + One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] + For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MonitorAddEntitiesResponse + Success + + Examples + -------- + from arcmira import Arcmira + + client = Arcmira( + api_key="YOUR_API_KEY", + ) + client.monitors.entities.add( + id="id", + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + entity_ids=["entity_ids"], + ) + """ + _response = self._raw_client.add( + id, + entity_ids=entity_ids, + idempotency_key=idempotency_key, + person_match_mode=person_match_mode, + request_options=request_options, + ) + return _response.data + + +class AsyncEntitiesClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._raw_client = AsyncRawEntitiesClient(client_wrapper=client_wrapper) + + @property + def with_raw_response(self) -> AsyncRawEntitiesClient: + """ + Retrieves a raw implementation of this client that returns raw responses. + + Returns + ------- + AsyncRawEntitiesClient + """ + return self._raw_client + + async def add( + self, + id: str, + *, + entity_ids: typing.Sequence[str], + idempotency_key: typing.Optional[str] = None, + person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> MonitorAddEntitiesResponse: + """ + Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + + Parameters + ---------- + id : str + Monitor id. + + entity_ids : typing.Sequence[str] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. + + idempotency_key : typing.Optional[str] + One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] + For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + MonitorAddEntitiesResponse + Success + + Examples + -------- + import asyncio + + from arcmira import AsyncArcmira + + client = AsyncArcmira( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.monitors.entities.add( + id="id", + idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", + entity_ids=["entity_ids"], + ) + + + asyncio.run(main()) + """ + _response = await self._raw_client.add( + id, + entity_ids=entity_ids, + idempotency_key=idempotency_key, + person_match_mode=person_match_mode, + request_options=request_options, + ) + return _response.data diff --git a/src/arcmira/entities/mentions/raw_client.py b/src/arcmira/monitors/entities/raw_client.py similarity index 56% rename from src/arcmira/entities/mentions/raw_client.py rename to src/arcmira/monitors/entities/raw_client.py index 50f18a5..961924a 100644 --- a/src/arcmira/entities/mentions/raw_client.py +++ b/src/arcmira/monitors/entities/raw_client.py @@ -5,127 +5,89 @@ from ...core.api_error import ApiError from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper +from ...core.http_response import AsyncHttpResponse, HttpResponse from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager from ...core.parse_error import ParsingError from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions from ...errors.bad_request_error import BadRequestError +from ...errors.conflict_error import ConflictError from ...errors.forbidden_error import ForbiddenError from ...errors.internal_server_error import InternalServerError from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError from ...errors.too_many_requests_error import TooManyRequestsError from ...errors.unauthorized_error import UnauthorizedError from ...types.error import Error -from ...types.mention import Mention -from ...types.mention_list_response import MentionListResponse -from .types.list_mentions_request_details import ListMentionsRequestDetails -from .types.list_mentions_request_sentiment import ListMentionsRequestSentiment +from ...types.monitor_add_entities_response import MonitorAddEntitiesResponse +from .types.add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode from pydantic import ValidationError +# this is used as the default value for optional parameters +OMIT = typing.cast(typing.Any, ...) -class RawMentionsClient: + +class RawEntitiesClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._client_wrapper = client_wrapper - def list( + def add( self, id: str, *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - q: typing.Optional[str] = None, - sentiment: typing.Optional[ListMentionsRequestSentiment] = None, - is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - details: typing.Optional[ListMentionsRequestDetails] = None, + entity_ids: typing.Sequence[str], + idempotency_key: typing.Optional[str] = None, + person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[Mention, MentionListResponse]: + ) -> HttpResponse[MonitorAddEntitiesResponse]: """ - Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] + Monitor id. - q : typing.Optional[str] + entity_ids : typing.Sequence[str] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - sentiment : typing.Optional[ListMentionsRequestSentiment] + idempotency_key : typing.Optional[str] + One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - is_appearance : typing.Optional[bool] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - details : typing.Optional[ListMentionsRequestDetails] + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] + For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[Mention, MentionListResponse] + HttpResponse[MonitorAddEntitiesResponse] Success """ _response = self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}/mentions", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "channel_id": channel_id, - "channel_name": channel_name, - "q": q, - "sentiment": sentiment, - "is_appearance": is_appearance, - "date_from": date_from, - "date_to": date_to, - "details": details, + f"v1/monitors/{encode_path_param(id)}/entities", + method="POST", + json={ + "entity_ids": entity_ids, + "person_match_mode": person_match_mode, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - MentionListResponse, + _data = typing.cast( + MonitorAddEntitiesResponse, parse_obj_as( - type_=MentionListResponse, # type: ignore + type_=MonitorAddEntitiesResponse, # type: ignore object_=_response.json(), ), ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list( - id, - limit=limit, - cursor=_parsed_next, - channel_id=channel_id, - channel_name=channel_name, - q=q, - sentiment=sentiment, - is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, - details=details, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + return HttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -148,8 +110,8 @@ def list( ), ), ) - if _response.status_code == 402: - raise PaymentRequiredError( + if _response.status_code == 403: + raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( Error, @@ -159,8 +121,8 @@ def list( ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( Error, @@ -170,8 +132,8 @@ def list( ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 409: + raise ConflictError( headers=dict(_response.headers), body=typing.cast( Error, @@ -213,110 +175,68 @@ def list( raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) -class AsyncRawMentionsClient: +class AsyncRawEntitiesClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._client_wrapper = client_wrapper - async def list( + async def add( self, id: str, *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - q: typing.Optional[str] = None, - sentiment: typing.Optional[ListMentionsRequestSentiment] = None, - is_appearance: typing.Optional[bool] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, - details: typing.Optional[ListMentionsRequestDetails] = None, + entity_ids: typing.Sequence[str], + idempotency_key: typing.Optional[str] = None, + person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[Mention, MentionListResponse]: + ) -> AsyncHttpResponse[MonitorAddEntitiesResponse]: """ - Cursor-paginated mentions for one entity, newest media first. Read timestamps from start_seconds / end_seconds (integer seconds; 0 means full episode); the MM:SS (or HH:MM:SS) string fields are deprecated. is_appearance filtering applies to person entities only; passing is_appearance=true for any other type returns a 400 (appearances_person_only). details=full attaches per-mention commercial recommendations and requires a Pro+ plan. + Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str - Entity id, ent_{n} or the numeric id. Merged ids follow their redirect. + Monitor id. - limit : typing.Optional[int] + entity_ids : typing.Sequence[str] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. + idempotency_key : typing.Optional[str] + One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. - channel_id : typing.Optional[str] - - channel_name : typing.Optional[str] - - q : typing.Optional[str] - - sentiment : typing.Optional[ListMentionsRequestSentiment] - - is_appearance : typing.Optional[bool] - - date_from : typing.Optional[str] - - date_to : typing.Optional[str] - - details : typing.Optional[ListMentionsRequestDetails] + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] + For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - AsyncPager[Mention, MentionListResponse] + AsyncHttpResponse[MonitorAddEntitiesResponse] Success """ _response = await self._client_wrapper.httpx_client.request( - f"v1/entities/{encode_path_param(id)}/mentions", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "channel_id": channel_id, - "channel_name": channel_name, - "q": q, - "sentiment": sentiment, - "is_appearance": is_appearance, - "date_from": date_from, - "date_to": date_to, - "details": details, + f"v1/monitors/{encode_path_param(id)}/entities", + method="POST", + json={ + "entity_ids": entity_ids, + "person_match_mode": person_match_mode, + }, + headers={ + "content-type": "application/json", + "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, }, request_options=request_options, + omit=OMIT, ) try: if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - MentionListResponse, + _data = typing.cast( + MonitorAddEntitiesResponse, parse_obj_as( - type_=MentionListResponse, # type: ignore + type_=MonitorAddEntitiesResponse, # type: ignore object_=_response.json(), ), ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.list( - id, - limit=limit, - cursor=_parsed_next, - channel_id=channel_id, - channel_name=channel_name, - q=q, - sentiment=sentiment, - is_appearance=is_appearance, - date_from=date_from, - date_to=date_to, - details=details, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -339,8 +259,8 @@ async def _get_next(): ), ), ) - if _response.status_code == 402: - raise PaymentRequiredError( + if _response.status_code == 403: + raise ForbiddenError( headers=dict(_response.headers), body=typing.cast( Error, @@ -350,8 +270,8 @@ async def _get_next(): ), ), ) - if _response.status_code == 403: - raise ForbiddenError( + if _response.status_code == 404: + raise NotFoundError( headers=dict(_response.headers), body=typing.cast( Error, @@ -361,8 +281,8 @@ async def _get_next(): ), ), ) - if _response.status_code == 404: - raise NotFoundError( + if _response.status_code == 409: + raise ConflictError( headers=dict(_response.headers), body=typing.cast( Error, diff --git a/src/arcmira/entities/mentions/__init__.py b/src/arcmira/monitors/entities/types/__init__.py similarity index 79% rename from src/arcmira/entities/mentions/__init__.py rename to src/arcmira/monitors/entities/types/__init__.py index 9b17658..74501f3 100644 --- a/src/arcmira/entities/mentions/__init__.py +++ b/src/arcmira/monitors/entities/types/__init__.py @@ -6,10 +6,9 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ListMentionsRequestDetails, ListMentionsRequestSentiment + from .add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode _dynamic_imports: typing.Dict[str, str] = { - "ListMentionsRequestDetails": ".types", - "ListMentionsRequestSentiment": ".types", + "AddEntitiesRequestPersonMatchMode": ".add_entities_request_person_match_mode" } @@ -34,4 +33,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ListMentionsRequestDetails", "ListMentionsRequestSentiment"] +__all__ = ["AddEntitiesRequestPersonMatchMode"] diff --git a/src/arcmira/monitors/entities/types/add_entities_request_person_match_mode.py b/src/arcmira/monitors/entities/types/add_entities_request_person_match_mode.py new file mode 100644 index 0000000..0c0c54a --- /dev/null +++ b/src/arcmira/monitors/entities/types/add_entities_request_person_match_mode.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AddEntitiesRequestPersonMatchMode = typing.Union[typing.Literal["mentions", "appearances", "both"], typing.Any] diff --git a/src/arcmira/monitors/raw_client.py b/src/arcmira/monitors/raw_client.py index b82b9a5..e4241d5 100644 --- a/src/arcmira/monitors/raw_client.py +++ b/src/arcmira/monitors/raw_client.py @@ -152,10 +152,11 @@ def create( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, + team_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MonitorMutationResponse]: """ - Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint. + Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint. Parameters ---------- @@ -178,7 +179,7 @@ def create( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -192,6 +193,9 @@ def create( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. + team_id : typing.Optional[str] + Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -205,15 +209,16 @@ def create( method="POST", json={ "name": name, - "notifyEmails": notify_emails, - "notifyFrequency": notify_frequency, - "digestDay": digest_day, - "digestTime": digest_time, - "notifyWebhook": notify_webhook, - "webhookUrl": webhook_url, - "notifySlack": notify_slack, - "slackIntegrationId": slack_integration_id, - "slackChannelId": slack_channel_id, + "notify_emails": notify_emails, + "notify_frequency": notify_frequency, + "digest_day": digest_day, + "digest_time": digest_time, + "notify_webhook": notify_webhook, + "webhook_url": webhook_url, + "notify_slack": notify_slack, + "slack_integration_id": slack_integration_id, + "slack_channel_id": slack_channel_id, + "team_id": team_id, }, headers={ "content-type": "application/json", @@ -326,7 +331,7 @@ def delete( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MonitorDeleteResponse]: """ - Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. + Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. Parameters ---------- @@ -463,13 +468,11 @@ def update( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, - is_paused: typing.Optional[bool] = OMIT, - is_collapsed: typing.Optional[bool] = OMIT, - sort_order: typing.Optional[int] = OMIT, + paused: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MonitorMutationResponse]: """ - A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. + A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. Parameters ---------- @@ -495,7 +498,7 @@ def update( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -509,15 +512,9 @@ def update( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. - is_paused : typing.Optional[bool] + paused : typing.Optional[bool] Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued. - is_collapsed : typing.Optional[bool] - Dashboard display state. - - sort_order : typing.Optional[int] - Dashboard sort position. - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -531,18 +528,16 @@ def update( method="PATCH", json={ "name": name, - "notifyEmails": notify_emails, - "notifyFrequency": notify_frequency, - "digestDay": digest_day, - "digestTime": digest_time, - "notifyWebhook": notify_webhook, - "webhookUrl": webhook_url, - "notifySlack": notify_slack, - "slackIntegrationId": slack_integration_id, - "slackChannelId": slack_channel_id, - "isPaused": is_paused, - "isCollapsed": is_collapsed, - "sortOrder": sort_order, + "notify_emails": notify_emails, + "notify_frequency": notify_frequency, + "digest_day": digest_day, + "digest_time": digest_time, + "notify_webhook": notify_webhook, + "webhook_url": webhook_url, + "notify_slack": notify_slack, + "slack_integration_id": slack_integration_id, + "slack_channel_id": slack_channel_id, + "paused": paused, }, headers={ "content-type": "application/json", @@ -655,7 +650,7 @@ def rotate_webhook_secret( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[WebhookSecretRotateResponse]: """ - Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope. + Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope. Parameters ---------- @@ -902,10 +897,11 @@ async def create( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, + team_id: typing.Optional[str] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MonitorMutationResponse]: """ - Creating with notifyWebhook: true and a webhookUrl enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhookSecretSet and webhookSecretHint. + Creating with notify_webhook: true and a webhook_url enables HMAC-signed webhook delivery and returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. All subsequent reads expose only webhook_secret_set and webhook_secret_hint. Parameters ---------- @@ -928,7 +924,7 @@ async def create( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -942,6 +938,9 @@ async def create( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. + team_id : typing.Optional[str] + Create the monitor in this team, which the caller must belong to. The team owner pays for it and its plan sets the limits. A member may not set a webhook. Create only. + request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -955,15 +954,16 @@ async def create( method="POST", json={ "name": name, - "notifyEmails": notify_emails, - "notifyFrequency": notify_frequency, - "digestDay": digest_day, - "digestTime": digest_time, - "notifyWebhook": notify_webhook, - "webhookUrl": webhook_url, - "notifySlack": notify_slack, - "slackIntegrationId": slack_integration_id, - "slackChannelId": slack_channel_id, + "notify_emails": notify_emails, + "notify_frequency": notify_frequency, + "digest_day": digest_day, + "digest_time": digest_time, + "notify_webhook": notify_webhook, + "webhook_url": webhook_url, + "notify_slack": notify_slack, + "slack_integration_id": slack_integration_id, + "slack_channel_id": slack_channel_id, + "team_id": team_id, }, headers={ "content-type": "application/json", @@ -1076,7 +1076,7 @@ async def delete( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MonitorDeleteResponse]: """ - Deletes the monitor AND every tracker inside it (trackersDeleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. + Deletes the monitor AND every tracker inside it (trackers_deleted reports how many). Cannot be undone. Retrying with the original Idempotency-Key returns the original deleted count without deleting again. Parameters ---------- @@ -1213,13 +1213,11 @@ async def update( notify_slack: typing.Optional[bool] = OMIT, slack_integration_id: typing.Optional[str] = OMIT, slack_channel_id: typing.Optional[str] = OMIT, - is_paused: typing.Optional[bool] = OMIT, - is_collapsed: typing.Optional[bool] = OMIT, - sort_order: typing.Optional[int] = OMIT, + paused: typing.Optional[bool] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MonitorMutationResponse]: """ - A PATCH that newly enables webhook signing (turns notifyWebhook on, or sets a webhookUrl where no secret existed before) returns the signing secret (monitor.webhookSecret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhookSecretSet and webhookSecretHint. PATCHing notifyWebhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. + A PATCH that newly enables webhook signing (turns notify_webhook on, or sets a webhook_url where no secret existed before) returns the signing secret (monitor.webhook_secret) in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Unrelated PATCHes expose only webhook_secret_set and webhook_secret_hint. PATCHing notify_webhook: true also re-enables a webhook that was auto-disabled after repeated failures and resets its failure counter. Parameters ---------- @@ -1245,7 +1243,7 @@ async def update( Digest send hour as HH:MM (account timezone). Default "09:00". Applies to daily digests. notify_webhook : typing.Optional[bool] - Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhookUrl, the response returns the signing secret (monitor.webhookSecret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. + Enable HMAC-signed webhook delivery (paid plans). When enabled together with webhook_url, the response returns the signing secret (monitor.webhook_secret), recoverable with the original Idempotency-Key during the valid recovery window. PATCHing true also re-enables an auto-disabled webhook and resets its failure counter. webhook_url : typing.Optional[str] Destination URL for webhook alert deliveries. @@ -1259,15 +1257,9 @@ async def update( slack_channel_id : typing.Optional[str] Slack channel id to deliver to. - is_paused : typing.Optional[bool] + paused : typing.Optional[bool] Paused monitors accept config changes but do not deliver; alerts that would have fired are not queued. - is_collapsed : typing.Optional[bool] - Dashboard display state. - - sort_order : typing.Optional[int] - Dashboard sort position. - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -1281,18 +1273,16 @@ async def update( method="PATCH", json={ "name": name, - "notifyEmails": notify_emails, - "notifyFrequency": notify_frequency, - "digestDay": digest_day, - "digestTime": digest_time, - "notifyWebhook": notify_webhook, - "webhookUrl": webhook_url, - "notifySlack": notify_slack, - "slackIntegrationId": slack_integration_id, - "slackChannelId": slack_channel_id, - "isPaused": is_paused, - "isCollapsed": is_collapsed, - "sortOrder": sort_order, + "notify_emails": notify_emails, + "notify_frequency": notify_frequency, + "digest_day": digest_day, + "digest_time": digest_time, + "notify_webhook": notify_webhook, + "webhook_url": webhook_url, + "notify_slack": notify_slack, + "slack_integration_id": slack_integration_id, + "slack_channel_id": slack_channel_id, + "paused": paused, }, headers={ "content-type": "application/json", @@ -1405,7 +1395,7 @@ async def rotate_webhook_secret( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[WebhookSecretRotateResponse]: """ - Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previousSecretExpiresAt (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhookUrl set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notifyWebhook: true. Requires the monitors:write scope. + Generates a new signing secret and returns it in this response. Store it securely. A retry with the original Idempotency-Key recovers the same secret for up to 24 hours while it remains the current secret or the valid previous secret. An expired or displaced secret returns 409 idempotency_result_expired without rotating again. Reads do not expose the secret. Zero-downtime overlap: the previous secret remains valid until previous_secret_expires_at (24 hours); during the window every delivery carries an additional X-Arcmira-Signature-Previous header computed with the old secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. After the window the old secret is dropped and the extra header disappears. Rotating again during the window replaces the previous secret and resets the window. Requires a configured webhook (webhook_url set); otherwise 409 with code webhook_not_configured. Auto-disable interplay: rotation resets webhook_failures but never re-enables a webhook that was auto-disabled after repeated failures; to resume delivery, also PATCH the monitor with notify_webhook: true. Requires the monitors:write scope. Parameters ---------- diff --git a/src/arcmira/monitors/trackers/client.py b/src/arcmira/monitors/trackers/client.py index 11f523a..ab2af81 100644 --- a/src/arcmira/monitors/trackers/client.py +++ b/src/arcmira/monitors/trackers/client.py @@ -65,7 +65,7 @@ def add( request_options: typing.Optional[RequestOptions] = None, ) -> MonitorAddTrackersResponse: """ - Attaches EXISTING trackers to the monitor by id ({ trackerIds: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count. + Attaches EXISTING trackers to the monitor by id ({ tracker_ids: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count. Parameters ---------- @@ -96,7 +96,7 @@ def add( client.monitors.trackers.add( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - tracker_ids=["trackerIds"], + tracker_ids=["tracker_ids"], ) """ _response = self._raw_client.add( @@ -168,7 +168,7 @@ async def add( request_options: typing.Optional[RequestOptions] = None, ) -> MonitorAddTrackersResponse: """ - Attaches EXISTING trackers to the monitor by id ({ trackerIds: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count. + Attaches EXISTING trackers to the monitor by id ({ tracker_ids: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count. Parameters ---------- @@ -204,7 +204,7 @@ async def main() -> None: await client.monitors.trackers.add( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - tracker_ids=["trackerIds"], + tracker_ids=["tracker_ids"], ) diff --git a/src/arcmira/monitors/trackers/raw_client.py b/src/arcmira/monitors/trackers/raw_client.py index 62073b2..cae3969 100644 --- a/src/arcmira/monitors/trackers/raw_client.py +++ b/src/arcmira/monitors/trackers/raw_client.py @@ -146,7 +146,7 @@ def add( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MonitorAddTrackersResponse]: """ - Attaches EXISTING trackers to the monitor by id ({ trackerIds: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count. + Attaches EXISTING trackers to the monitor by id ({ tracker_ids: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count. Parameters ---------- @@ -171,7 +171,7 @@ def add( f"v1/monitors/{encode_path_param(id)}/trackers", method="POST", json={ - "trackerIds": tracker_ids, + "tracker_ids": tracker_ids, }, headers={ "content-type": "application/json", @@ -397,7 +397,7 @@ async def add( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MonitorAddTrackersResponse]: """ - Attaches EXISTING trackers to the monitor by id ({ trackerIds: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attachedCount reports the unique attached count. + Attaches EXISTING trackers to the monitor by id ({ tracker_ids: ["trk_..."] }). It does not create trackers: create them first via POST /v1/trackers, then attach. Attached trackers use the monitor's delivery settings. Supply 1 to 90 IDs. Duplicate IDs count once. Every ID must belong to the account; a missing or foreign ID returns tracker_not_found and none are attached. attached_count reports the unique attached count. Parameters ---------- @@ -422,7 +422,7 @@ async def add( f"v1/monitors/{encode_path_param(id)}/trackers", method="POST", json={ - "trackerIds": tracker_ids, + "tracker_ids": tracker_ids, }, headers={ "content-type": "application/json", diff --git a/src/arcmira/organizations/__init__.py b/src/arcmira/organizations/__init__.py deleted file mode 100644 index c556990..0000000 --- a/src/arcmira/organizations/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from . import related - from .related import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".related", - "ChannelsRelatedRequestMode": ".related", - "ChannelsRelatedRequestOrder": ".related", - "OrganizationsRelatedRequestIsAppearance": ".related", - "OrganizationsRelatedRequestMode": ".related", - "OrganizationsRelatedRequestOrder": ".related", - "PeopleRelatedRequestIsAppearance": ".related", - "PeopleRelatedRequestMode": ".related", - "PeopleRelatedRequestOrder": ".related", - "ProductsRelatedRequestIsAppearance": ".related", - "ProductsRelatedRequestMode": ".related", - "ProductsRelatedRequestOrder": ".related", - "TopicsRelatedRequestIsAppearance": ".related", - "TopicsRelatedRequestMode": ".related", - "TopicsRelatedRequestOrder": ".related", - "related": ".related", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", - "related", -] diff --git a/src/arcmira/organizations/client.py b/src/arcmira/organizations/client.py deleted file mode 100644 index c62de42..0000000 --- a/src/arcmira/organizations/client.py +++ /dev/null @@ -1,133 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.organization_page_response import OrganizationPageResponse -from .raw_client import AsyncRawOrganizationsClient, RawOrganizationsClient - -if typing.TYPE_CHECKING: - from .related.client import AsyncRelatedClient, RelatedClient - - -class OrganizationsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawOrganizationsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[RelatedClient] = None - - @property - def with_raw_response(self) -> RawOrganizationsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawOrganizationsClient - """ - return self._raw_client - - def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> OrganizationPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationPageResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.organizations.get( - slug="slug", - ) - """ - _response = self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import RelatedClient # noqa: E402 - - self._related = RelatedClient(client_wrapper=self._client_wrapper) - return self._related - - -class AsyncOrganizationsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawOrganizationsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[AsyncRelatedClient] = None - - @property - def with_raw_response(self) -> AsyncRawOrganizationsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawOrganizationsClient - """ - return self._raw_client - - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> OrganizationPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - OrganizationPageResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.organizations.get( - slug="slug", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import AsyncRelatedClient # noqa: E402 - - self._related = AsyncRelatedClient(client_wrapper=self._client_wrapper) - return self._related diff --git a/src/arcmira/organizations/raw_client.py b/src/arcmira/organizations/raw_client.py deleted file mode 100644 index 4686295..0000000 --- a/src/arcmira/organizations/raw_client.py +++ /dev/null @@ -1,268 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import encode_path_param -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.payment_required_error import PaymentRequiredError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.error import Error -from ..types.organization_page_response import OrganizationPageResponse -from pydantic import ValidationError - - -class RawOrganizationsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[OrganizationPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[OrganizationPageResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - OrganizationPageResponse, - parse_obj_as( - type_=OrganizationPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawOrganizationsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[OrganizationPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[OrganizationPageResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - OrganizationPageResponse, - parse_obj_as( - type_=OrganizationPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/organizations/related/__init__.py b/src/arcmira/organizations/related/__init__.py deleted file mode 100644 index bed85d9..0000000 --- a/src/arcmira/organizations/related/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".types", - "ChannelsRelatedRequestMode": ".types", - "ChannelsRelatedRequestOrder": ".types", - "OrganizationsRelatedRequestIsAppearance": ".types", - "OrganizationsRelatedRequestMode": ".types", - "OrganizationsRelatedRequestOrder": ".types", - "PeopleRelatedRequestIsAppearance": ".types", - "PeopleRelatedRequestMode": ".types", - "PeopleRelatedRequestOrder": ".types", - "ProductsRelatedRequestIsAppearance": ".types", - "ProductsRelatedRequestMode": ".types", - "ProductsRelatedRequestOrder": ".types", - "TopicsRelatedRequestIsAppearance": ".types", - "TopicsRelatedRequestMode": ".types", - "TopicsRelatedRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/organizations/related/client.py b/src/arcmira/organizations/related/client.py deleted file mode 100644 index a48fe3e..0000000 --- a/src/arcmira/organizations/related/client.py +++ /dev/null @@ -1,930 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .raw_client import AsyncRawRelatedClient, RawRelatedClient -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder - - -class RelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRelatedClient - """ - return self._raw_client - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.organizations.related.topics( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.organizations.related.people( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.organizations.related.organizations( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.organizations.related.products( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.organizations.related.channels( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRelatedClient - """ - return self._raw_client - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.organizations.related.topics( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.organizations.related.people( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.organizations.related.organizations( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.organizations.related.products( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.organizations.related.channels( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/organizations/related/raw_client.py b/src/arcmira/organizations/related/raw_client.py deleted file mode 100644 index 22a2ccb..0000000 --- a/src/arcmira/organizations/related/raw_client.py +++ /dev/null @@ -1,1861 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from ...types.error import Error -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder -from pydantic import ValidationError - - -class RawRelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this organization in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/organizations/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/organizations/related/types/__init__.py b/src/arcmira/organizations/related/types/__init__.py deleted file mode 100644 index 1f3a8a7..0000000 --- a/src/arcmira/organizations/related/types/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance - from .channels_related_request_mode import ChannelsRelatedRequestMode - from .channels_related_request_order import ChannelsRelatedRequestOrder - from .organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance - from .organizations_related_request_mode import OrganizationsRelatedRequestMode - from .organizations_related_request_order import OrganizationsRelatedRequestOrder - from .people_related_request_is_appearance import PeopleRelatedRequestIsAppearance - from .people_related_request_mode import PeopleRelatedRequestMode - from .people_related_request_order import PeopleRelatedRequestOrder - from .products_related_request_is_appearance import ProductsRelatedRequestIsAppearance - from .products_related_request_mode import ProductsRelatedRequestMode - from .products_related_request_order import ProductsRelatedRequestOrder - from .topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance - from .topics_related_request_mode import TopicsRelatedRequestMode - from .topics_related_request_order import TopicsRelatedRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".channels_related_request_is_appearance", - "ChannelsRelatedRequestMode": ".channels_related_request_mode", - "ChannelsRelatedRequestOrder": ".channels_related_request_order", - "OrganizationsRelatedRequestIsAppearance": ".organizations_related_request_is_appearance", - "OrganizationsRelatedRequestMode": ".organizations_related_request_mode", - "OrganizationsRelatedRequestOrder": ".organizations_related_request_order", - "PeopleRelatedRequestIsAppearance": ".people_related_request_is_appearance", - "PeopleRelatedRequestMode": ".people_related_request_mode", - "PeopleRelatedRequestOrder": ".people_related_request_order", - "ProductsRelatedRequestIsAppearance": ".products_related_request_is_appearance", - "ProductsRelatedRequestMode": ".products_related_request_mode", - "ProductsRelatedRequestOrder": ".products_related_request_order", - "TopicsRelatedRequestIsAppearance": ".topics_related_request_is_appearance", - "TopicsRelatedRequestMode": ".topics_related_request_mode", - "TopicsRelatedRequestOrder": ".topics_related_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/organizations/related/types/channels_related_request_is_appearance.py b/src/arcmira/organizations/related/types/channels_related_request_is_appearance.py deleted file mode 100644 index e21cd2c..0000000 --- a/src/arcmira/organizations/related/types/channels_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/organizations/related/types/channels_related_request_mode.py b/src/arcmira/organizations/related/types/channels_related_request_mode.py deleted file mode 100644 index 49a4137..0000000 --- a/src/arcmira/organizations/related/types/channels_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/organizations/related/types/channels_related_request_order.py b/src/arcmira/organizations/related/types/channels_related_request_order.py deleted file mode 100644 index 0f5b302..0000000 --- a/src/arcmira/organizations/related/types/channels_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/organizations/related/types/organizations_related_request_is_appearance.py b/src/arcmira/organizations/related/types/organizations_related_request_is_appearance.py deleted file mode 100644 index 2762d60..0000000 --- a/src/arcmira/organizations/related/types/organizations_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/organizations/related/types/organizations_related_request_mode.py b/src/arcmira/organizations/related/types/organizations_related_request_mode.py deleted file mode 100644 index 2bdd64b..0000000 --- a/src/arcmira/organizations/related/types/organizations_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/organizations/related/types/organizations_related_request_order.py b/src/arcmira/organizations/related/types/organizations_related_request_order.py deleted file mode 100644 index 4c5dde2..0000000 --- a/src/arcmira/organizations/related/types/organizations_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/organizations/related/types/people_related_request_is_appearance.py b/src/arcmira/organizations/related/types/people_related_request_is_appearance.py deleted file mode 100644 index af591fc..0000000 --- a/src/arcmira/organizations/related/types/people_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/organizations/related/types/people_related_request_mode.py b/src/arcmira/organizations/related/types/people_related_request_mode.py deleted file mode 100644 index 9d9b51c..0000000 --- a/src/arcmira/organizations/related/types/people_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/organizations/related/types/people_related_request_order.py b/src/arcmira/organizations/related/types/people_related_request_order.py deleted file mode 100644 index a0d19ad..0000000 --- a/src/arcmira/organizations/related/types/people_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/organizations/related/types/products_related_request_is_appearance.py b/src/arcmira/organizations/related/types/products_related_request_is_appearance.py deleted file mode 100644 index a6aba02..0000000 --- a/src/arcmira/organizations/related/types/products_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/organizations/related/types/products_related_request_mode.py b/src/arcmira/organizations/related/types/products_related_request_mode.py deleted file mode 100644 index 9046641..0000000 --- a/src/arcmira/organizations/related/types/products_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/organizations/related/types/products_related_request_order.py b/src/arcmira/organizations/related/types/products_related_request_order.py deleted file mode 100644 index 3e5eb9f..0000000 --- a/src/arcmira/organizations/related/types/products_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/organizations/related/types/topics_related_request_is_appearance.py b/src/arcmira/organizations/related/types/topics_related_request_is_appearance.py deleted file mode 100644 index 2b7c65e..0000000 --- a/src/arcmira/organizations/related/types/topics_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/organizations/related/types/topics_related_request_mode.py b/src/arcmira/organizations/related/types/topics_related_request_mode.py deleted file mode 100644 index 090ee69..0000000 --- a/src/arcmira/organizations/related/types/topics_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/organizations/related/types/topics_related_request_order.py b/src/arcmira/organizations/related/types/topics_related_request_order.py deleted file mode 100644 index 56c645a..0000000 --- a/src/arcmira/organizations/related/types/topics_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/__init__.py b/src/arcmira/people/__init__.py deleted file mode 100644 index f020fad..0000000 --- a/src/arcmira/people/__init__.py +++ /dev/null @@ -1,94 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from . import appearances, related - from .appearances import ListAppearancesRequestIsAppearance, ListAppearancesRequestMode, ListAppearancesRequestOrder - from .related import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".related", - "ChannelsRelatedRequestMode": ".related", - "ChannelsRelatedRequestOrder": ".related", - "ListAppearancesRequestIsAppearance": ".appearances", - "ListAppearancesRequestMode": ".appearances", - "ListAppearancesRequestOrder": ".appearances", - "OrganizationsRelatedRequestIsAppearance": ".related", - "OrganizationsRelatedRequestMode": ".related", - "OrganizationsRelatedRequestOrder": ".related", - "PeopleRelatedRequestIsAppearance": ".related", - "PeopleRelatedRequestMode": ".related", - "PeopleRelatedRequestOrder": ".related", - "ProductsRelatedRequestIsAppearance": ".related", - "ProductsRelatedRequestMode": ".related", - "ProductsRelatedRequestOrder": ".related", - "TopicsRelatedRequestIsAppearance": ".related", - "TopicsRelatedRequestMode": ".related", - "TopicsRelatedRequestOrder": ".related", - "appearances": ".appearances", - "related": ".related", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "ListAppearancesRequestIsAppearance", - "ListAppearancesRequestMode", - "ListAppearancesRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", - "appearances", - "related", -] diff --git a/src/arcmira/people/appearances/__init__.py b/src/arcmira/people/appearances/__init__.py deleted file mode 100644 index 74a583c..0000000 --- a/src/arcmira/people/appearances/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ListAppearancesRequestIsAppearance, ListAppearancesRequestMode, ListAppearancesRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ListAppearancesRequestIsAppearance": ".types", - "ListAppearancesRequestMode": ".types", - "ListAppearancesRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListAppearancesRequestIsAppearance", "ListAppearancesRequestMode", "ListAppearancesRequestOrder"] diff --git a/src/arcmira/people/appearances/client.py b/src/arcmira/people/appearances/client.py deleted file mode 100644 index a686de6..0000000 --- a/src/arcmira/people/appearances/client.py +++ /dev/null @@ -1,218 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.person_appearance_list_response import PersonAppearanceListResponse -from ...types.person_appearance_list_response_items_item import PersonAppearanceListResponseItemsItem -from .raw_client import AsyncRawAppearancesClient, RawAppearancesClient -from .types.list_appearances_request_is_appearance import ListAppearancesRequestIsAppearance -from .types.list_appearances_request_mode import ListAppearancesRequestMode -from .types.list_appearances_request_order import ListAppearancesRequestOrder - - -class AppearancesClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawAppearancesClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawAppearancesClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawAppearancesClient - """ - return self._raw_client - - def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListAppearancesRequestOrder] = None, - mode: typing.Optional[ListAppearancesRequestMode] = None, - is_appearance: typing.Optional[ListAppearancesRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse]: - """ - Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListAppearancesRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListAppearancesRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListAppearancesRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.appearances.list( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.list( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncAppearancesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawAppearancesClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawAppearancesClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawAppearancesClient - """ - return self._raw_client - - async def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListAppearancesRequestOrder] = None, - mode: typing.Optional[ListAppearancesRequestMode] = None, - is_appearance: typing.Optional[ListAppearancesRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse]: - """ - Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListAppearancesRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListAppearancesRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListAppearancesRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.appearances.list( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.list( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/people/appearances/raw_client.py b/src/arcmira/people/appearances/raw_client.py deleted file mode 100644 index b60c269..0000000 --- a/src/arcmira/people/appearances/raw_client.py +++ /dev/null @@ -1,397 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.person_appearance_list_response import PersonAppearanceListResponse -from ...types.person_appearance_list_response_items_item import PersonAppearanceListResponseItemsItem -from .types.list_appearances_request_is_appearance import ListAppearancesRequestIsAppearance -from .types.list_appearances_request_mode import ListAppearancesRequestMode -from .types.list_appearances_request_order import ListAppearancesRequestOrder -from pydantic import ValidationError - - -class RawAppearancesClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListAppearancesRequestOrder] = None, - mode: typing.Optional[ListAppearancesRequestMode] = None, - is_appearance: typing.Optional[ListAppearancesRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse]: - """ - Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListAppearancesRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListAppearancesRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListAppearancesRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/appearances", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - PersonAppearanceListResponse, - parse_obj_as( - type_=PersonAppearanceListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawAppearancesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ListAppearancesRequestOrder] = None, - mode: typing.Optional[ListAppearancesRequestMode] = None, - is_appearance: typing.Optional[ListAppearancesRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse]: - """ - Appearances (the person was actually present in the media) for one person, newest first. Person-only: the equivalent route for any other entity type returns a 400 (appearances_person_only). Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. The rows are display-oriented. For programmatic pagination, date filtering, and the standard mention-row shape, use GET /v1/mentions?entity_id=...&is_appearance=true instead. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ListAppearancesRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ListAppearancesRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ListAppearancesRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[PersonAppearanceListResponseItemsItem, PersonAppearanceListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/appearances", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - PersonAppearanceListResponse, - parse_obj_as( - type_=PersonAppearanceListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.list( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/people/appearances/types/__init__.py b/src/arcmira/people/appearances/types/__init__.py deleted file mode 100644 index 0b0790e..0000000 --- a/src/arcmira/people/appearances/types/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .list_appearances_request_is_appearance import ListAppearancesRequestIsAppearance - from .list_appearances_request_mode import ListAppearancesRequestMode - from .list_appearances_request_order import ListAppearancesRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ListAppearancesRequestIsAppearance": ".list_appearances_request_is_appearance", - "ListAppearancesRequestMode": ".list_appearances_request_mode", - "ListAppearancesRequestOrder": ".list_appearances_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = ["ListAppearancesRequestIsAppearance", "ListAppearancesRequestMode", "ListAppearancesRequestOrder"] diff --git a/src/arcmira/people/appearances/types/list_appearances_request_is_appearance.py b/src/arcmira/people/appearances/types/list_appearances_request_is_appearance.py deleted file mode 100644 index 1333b2f..0000000 --- a/src/arcmira/people/appearances/types/list_appearances_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListAppearancesRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/appearances/types/list_appearances_request_mode.py b/src/arcmira/people/appearances/types/list_appearances_request_mode.py deleted file mode 100644 index 98be017..0000000 --- a/src/arcmira/people/appearances/types/list_appearances_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListAppearancesRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/appearances/types/list_appearances_request_order.py b/src/arcmira/people/appearances/types/list_appearances_request_order.py deleted file mode 100644 index ac5ac90..0000000 --- a/src/arcmira/people/appearances/types/list_appearances_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListAppearancesRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/client.py b/src/arcmira/people/client.py deleted file mode 100644 index 48e7816..0000000 --- a/src/arcmira/people/client.py +++ /dev/null @@ -1,150 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.person_page_response import PersonPageResponse -from .raw_client import AsyncRawPeopleClient, RawPeopleClient - -if typing.TYPE_CHECKING: - from .appearances.client import AppearancesClient, AsyncAppearancesClient - from .related.client import AsyncRelatedClient, RelatedClient - - -class PeopleClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawPeopleClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._appearances: typing.Optional[AppearancesClient] = None - self._related: typing.Optional[RelatedClient] = None - - @property - def with_raw_response(self) -> RawPeopleClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawPeopleClient - """ - return self._raw_client - - def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> PersonPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PersonPageResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.people.get( - slug="slug", - ) - """ - _response = self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def appearances(self): - if self._appearances is None: - from .appearances.client import AppearancesClient # noqa: E402 - - self._appearances = AppearancesClient(client_wrapper=self._client_wrapper) - return self._appearances - - @property - def related(self): - if self._related is None: - from .related.client import RelatedClient # noqa: E402 - - self._related = RelatedClient(client_wrapper=self._client_wrapper) - return self._related - - -class AsyncPeopleClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawPeopleClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._appearances: typing.Optional[AsyncAppearancesClient] = None - self._related: typing.Optional[AsyncRelatedClient] = None - - @property - def with_raw_response(self) -> AsyncRawPeopleClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawPeopleClient - """ - return self._raw_client - - async def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> PersonPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - PersonPageResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.people.get( - slug="slug", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def appearances(self): - if self._appearances is None: - from .appearances.client import AsyncAppearancesClient # noqa: E402 - - self._appearances = AsyncAppearancesClient(client_wrapper=self._client_wrapper) - return self._appearances - - @property - def related(self): - if self._related is None: - from .related.client import AsyncRelatedClient # noqa: E402 - - self._related = AsyncRelatedClient(client_wrapper=self._client_wrapper) - return self._related diff --git a/src/arcmira/people/raw_client.py b/src/arcmira/people/raw_client.py deleted file mode 100644 index 60f9b1f..0000000 --- a/src/arcmira/people/raw_client.py +++ /dev/null @@ -1,268 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import encode_path_param -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.payment_required_error import PaymentRequiredError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.error import Error -from ..types.person_page_response import PersonPageResponse -from pydantic import ValidationError - - -class RawPeopleClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[PersonPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[PersonPageResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - PersonPageResponse, - parse_obj_as( - type_=PersonPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawPeopleClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[PersonPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[PersonPageResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - PersonPageResponse, - parse_obj_as( - type_=PersonPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/people/related/__init__.py b/src/arcmira/people/related/__init__.py deleted file mode 100644 index bed85d9..0000000 --- a/src/arcmira/people/related/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".types", - "ChannelsRelatedRequestMode": ".types", - "ChannelsRelatedRequestOrder": ".types", - "OrganizationsRelatedRequestIsAppearance": ".types", - "OrganizationsRelatedRequestMode": ".types", - "OrganizationsRelatedRequestOrder": ".types", - "PeopleRelatedRequestIsAppearance": ".types", - "PeopleRelatedRequestMode": ".types", - "PeopleRelatedRequestOrder": ".types", - "ProductsRelatedRequestIsAppearance": ".types", - "ProductsRelatedRequestMode": ".types", - "ProductsRelatedRequestOrder": ".types", - "TopicsRelatedRequestIsAppearance": ".types", - "TopicsRelatedRequestMode": ".types", - "TopicsRelatedRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/people/related/client.py b/src/arcmira/people/related/client.py deleted file mode 100644 index 1582b2e..0000000 --- a/src/arcmira/people/related/client.py +++ /dev/null @@ -1,930 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .raw_client import AsyncRawRelatedClient, RawRelatedClient -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder - - -class RelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRelatedClient - """ - return self._raw_client - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.related.topics( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.related.people( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.related.organizations( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.related.products( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.people.related.channels( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRelatedClient - """ - return self._raw_client - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.related.topics( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.related.people( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.related.organizations( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.related.products( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.people.related.channels( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/people/related/raw_client.py b/src/arcmira/people/related/raw_client.py deleted file mode 100644 index d10b347..0000000 --- a/src/arcmira/people/related/raw_client.py +++ /dev/null @@ -1,1861 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from ...types.error import Error -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder -from pydantic import ValidationError - - -class RawRelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this person in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/people/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/people/related/types/__init__.py b/src/arcmira/people/related/types/__init__.py deleted file mode 100644 index 1f3a8a7..0000000 --- a/src/arcmira/people/related/types/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance - from .channels_related_request_mode import ChannelsRelatedRequestMode - from .channels_related_request_order import ChannelsRelatedRequestOrder - from .organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance - from .organizations_related_request_mode import OrganizationsRelatedRequestMode - from .organizations_related_request_order import OrganizationsRelatedRequestOrder - from .people_related_request_is_appearance import PeopleRelatedRequestIsAppearance - from .people_related_request_mode import PeopleRelatedRequestMode - from .people_related_request_order import PeopleRelatedRequestOrder - from .products_related_request_is_appearance import ProductsRelatedRequestIsAppearance - from .products_related_request_mode import ProductsRelatedRequestMode - from .products_related_request_order import ProductsRelatedRequestOrder - from .topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance - from .topics_related_request_mode import TopicsRelatedRequestMode - from .topics_related_request_order import TopicsRelatedRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".channels_related_request_is_appearance", - "ChannelsRelatedRequestMode": ".channels_related_request_mode", - "ChannelsRelatedRequestOrder": ".channels_related_request_order", - "OrganizationsRelatedRequestIsAppearance": ".organizations_related_request_is_appearance", - "OrganizationsRelatedRequestMode": ".organizations_related_request_mode", - "OrganizationsRelatedRequestOrder": ".organizations_related_request_order", - "PeopleRelatedRequestIsAppearance": ".people_related_request_is_appearance", - "PeopleRelatedRequestMode": ".people_related_request_mode", - "PeopleRelatedRequestOrder": ".people_related_request_order", - "ProductsRelatedRequestIsAppearance": ".products_related_request_is_appearance", - "ProductsRelatedRequestMode": ".products_related_request_mode", - "ProductsRelatedRequestOrder": ".products_related_request_order", - "TopicsRelatedRequestIsAppearance": ".topics_related_request_is_appearance", - "TopicsRelatedRequestMode": ".topics_related_request_mode", - "TopicsRelatedRequestOrder": ".topics_related_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/people/related/types/channels_related_request_is_appearance.py b/src/arcmira/people/related/types/channels_related_request_is_appearance.py deleted file mode 100644 index e21cd2c..0000000 --- a/src/arcmira/people/related/types/channels_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/related/types/channels_related_request_mode.py b/src/arcmira/people/related/types/channels_related_request_mode.py deleted file mode 100644 index 49a4137..0000000 --- a/src/arcmira/people/related/types/channels_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/related/types/channels_related_request_order.py b/src/arcmira/people/related/types/channels_related_request_order.py deleted file mode 100644 index 0f5b302..0000000 --- a/src/arcmira/people/related/types/channels_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/related/types/organizations_related_request_is_appearance.py b/src/arcmira/people/related/types/organizations_related_request_is_appearance.py deleted file mode 100644 index 2762d60..0000000 --- a/src/arcmira/people/related/types/organizations_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/related/types/organizations_related_request_mode.py b/src/arcmira/people/related/types/organizations_related_request_mode.py deleted file mode 100644 index 2bdd64b..0000000 --- a/src/arcmira/people/related/types/organizations_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/related/types/organizations_related_request_order.py b/src/arcmira/people/related/types/organizations_related_request_order.py deleted file mode 100644 index 4c5dde2..0000000 --- a/src/arcmira/people/related/types/organizations_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/related/types/people_related_request_is_appearance.py b/src/arcmira/people/related/types/people_related_request_is_appearance.py deleted file mode 100644 index af591fc..0000000 --- a/src/arcmira/people/related/types/people_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/related/types/people_related_request_mode.py b/src/arcmira/people/related/types/people_related_request_mode.py deleted file mode 100644 index 9d9b51c..0000000 --- a/src/arcmira/people/related/types/people_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/related/types/people_related_request_order.py b/src/arcmira/people/related/types/people_related_request_order.py deleted file mode 100644 index a0d19ad..0000000 --- a/src/arcmira/people/related/types/people_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/related/types/products_related_request_is_appearance.py b/src/arcmira/people/related/types/products_related_request_is_appearance.py deleted file mode 100644 index a6aba02..0000000 --- a/src/arcmira/people/related/types/products_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/related/types/products_related_request_mode.py b/src/arcmira/people/related/types/products_related_request_mode.py deleted file mode 100644 index 9046641..0000000 --- a/src/arcmira/people/related/types/products_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/related/types/products_related_request_order.py b/src/arcmira/people/related/types/products_related_request_order.py deleted file mode 100644 index 3e5eb9f..0000000 --- a/src/arcmira/people/related/types/products_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/people/related/types/topics_related_request_is_appearance.py b/src/arcmira/people/related/types/topics_related_request_is_appearance.py deleted file mode 100644 index 2b7c65e..0000000 --- a/src/arcmira/people/related/types/topics_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/people/related/types/topics_related_request_mode.py b/src/arcmira/people/related/types/topics_related_request_mode.py deleted file mode 100644 index 090ee69..0000000 --- a/src/arcmira/people/related/types/topics_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/people/related/types/topics_related_request_order.py b/src/arcmira/people/related/types/topics_related_request_order.py deleted file mode 100644 index 56c645a..0000000 --- a/src/arcmira/people/related/types/topics_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/products/__init__.py b/src/arcmira/products/__init__.py deleted file mode 100644 index c556990..0000000 --- a/src/arcmira/products/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from . import related - from .related import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".related", - "ChannelsRelatedRequestMode": ".related", - "ChannelsRelatedRequestOrder": ".related", - "OrganizationsRelatedRequestIsAppearance": ".related", - "OrganizationsRelatedRequestMode": ".related", - "OrganizationsRelatedRequestOrder": ".related", - "PeopleRelatedRequestIsAppearance": ".related", - "PeopleRelatedRequestMode": ".related", - "PeopleRelatedRequestOrder": ".related", - "ProductsRelatedRequestIsAppearance": ".related", - "ProductsRelatedRequestMode": ".related", - "ProductsRelatedRequestOrder": ".related", - "TopicsRelatedRequestIsAppearance": ".related", - "TopicsRelatedRequestMode": ".related", - "TopicsRelatedRequestOrder": ".related", - "related": ".related", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", - "related", -] diff --git a/src/arcmira/products/client.py b/src/arcmira/products/client.py deleted file mode 100644 index 75ee258..0000000 --- a/src/arcmira/products/client.py +++ /dev/null @@ -1,131 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.product_page_response import ProductPageResponse -from .raw_client import AsyncRawProductsClient, RawProductsClient - -if typing.TYPE_CHECKING: - from .related.client import AsyncRelatedClient, RelatedClient - - -class ProductsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawProductsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[RelatedClient] = None - - @property - def with_raw_response(self) -> RawProductsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawProductsClient - """ - return self._raw_client - - def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> ProductPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ProductPageResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.products.get( - slug="slug", - ) - """ - _response = self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import RelatedClient # noqa: E402 - - self._related = RelatedClient(client_wrapper=self._client_wrapper) - return self._related - - -class AsyncProductsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawProductsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[AsyncRelatedClient] = None - - @property - def with_raw_response(self) -> AsyncRawProductsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawProductsClient - """ - return self._raw_client - - async def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> ProductPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - ProductPageResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.products.get( - slug="slug", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import AsyncRelatedClient # noqa: E402 - - self._related = AsyncRelatedClient(client_wrapper=self._client_wrapper) - return self._related diff --git a/src/arcmira/products/raw_client.py b/src/arcmira/products/raw_client.py deleted file mode 100644 index 4ce3f7c..0000000 --- a/src/arcmira/products/raw_client.py +++ /dev/null @@ -1,268 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.jsonable_encoder import encode_path_param -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.payment_required_error import PaymentRequiredError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.error import Error -from ..types.product_page_response import ProductPageResponse -from pydantic import ValidationError - - -class RawProductsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[ProductPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[ProductPageResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ProductPageResponse, - parse_obj_as( - type_=ProductPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawProductsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def get( - self, slug: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[ProductPageResponse]: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[ProductPageResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - ProductPageResponse, - parse_obj_as( - type_=ProductPageResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/products/related/__init__.py b/src/arcmira/products/related/__init__.py deleted file mode 100644 index bed85d9..0000000 --- a/src/arcmira/products/related/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".types", - "ChannelsRelatedRequestMode": ".types", - "ChannelsRelatedRequestOrder": ".types", - "OrganizationsRelatedRequestIsAppearance": ".types", - "OrganizationsRelatedRequestMode": ".types", - "OrganizationsRelatedRequestOrder": ".types", - "PeopleRelatedRequestIsAppearance": ".types", - "PeopleRelatedRequestMode": ".types", - "PeopleRelatedRequestOrder": ".types", - "ProductsRelatedRequestIsAppearance": ".types", - "ProductsRelatedRequestMode": ".types", - "ProductsRelatedRequestOrder": ".types", - "TopicsRelatedRequestIsAppearance": ".types", - "TopicsRelatedRequestMode": ".types", - "TopicsRelatedRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/products/related/client.py b/src/arcmira/products/related/client.py deleted file mode 100644 index 2f83cd1..0000000 --- a/src/arcmira/products/related/client.py +++ /dev/null @@ -1,930 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .raw_client import AsyncRawRelatedClient, RawRelatedClient -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder - - -class RelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRelatedClient - """ - return self._raw_client - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.products.related.topics( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.products.related.people( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.products.related.organizations( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.products.related.products( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.products.related.channels( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRelatedClient - """ - return self._raw_client - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.products.related.topics( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.products.related.people( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.products.related.organizations( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.products.related.products( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.products.related.channels( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/products/related/raw_client.py b/src/arcmira/products/related/raw_client.py deleted file mode 100644 index 5b482e7..0000000 --- a/src/arcmira/products/related/raw_client.py +++ /dev/null @@ -1,1861 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from ...types.error import Error -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder -from pydantic import ValidationError - - -class RawRelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this product in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/products/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/products/related/types/__init__.py b/src/arcmira/products/related/types/__init__.py deleted file mode 100644 index 1f3a8a7..0000000 --- a/src/arcmira/products/related/types/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance - from .channels_related_request_mode import ChannelsRelatedRequestMode - from .channels_related_request_order import ChannelsRelatedRequestOrder - from .organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance - from .organizations_related_request_mode import OrganizationsRelatedRequestMode - from .organizations_related_request_order import OrganizationsRelatedRequestOrder - from .people_related_request_is_appearance import PeopleRelatedRequestIsAppearance - from .people_related_request_mode import PeopleRelatedRequestMode - from .people_related_request_order import PeopleRelatedRequestOrder - from .products_related_request_is_appearance import ProductsRelatedRequestIsAppearance - from .products_related_request_mode import ProductsRelatedRequestMode - from .products_related_request_order import ProductsRelatedRequestOrder - from .topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance - from .topics_related_request_mode import TopicsRelatedRequestMode - from .topics_related_request_order import TopicsRelatedRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".channels_related_request_is_appearance", - "ChannelsRelatedRequestMode": ".channels_related_request_mode", - "ChannelsRelatedRequestOrder": ".channels_related_request_order", - "OrganizationsRelatedRequestIsAppearance": ".organizations_related_request_is_appearance", - "OrganizationsRelatedRequestMode": ".organizations_related_request_mode", - "OrganizationsRelatedRequestOrder": ".organizations_related_request_order", - "PeopleRelatedRequestIsAppearance": ".people_related_request_is_appearance", - "PeopleRelatedRequestMode": ".people_related_request_mode", - "PeopleRelatedRequestOrder": ".people_related_request_order", - "ProductsRelatedRequestIsAppearance": ".products_related_request_is_appearance", - "ProductsRelatedRequestMode": ".products_related_request_mode", - "ProductsRelatedRequestOrder": ".products_related_request_order", - "TopicsRelatedRequestIsAppearance": ".topics_related_request_is_appearance", - "TopicsRelatedRequestMode": ".topics_related_request_mode", - "TopicsRelatedRequestOrder": ".topics_related_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/products/related/types/channels_related_request_is_appearance.py b/src/arcmira/products/related/types/channels_related_request_is_appearance.py deleted file mode 100644 index e21cd2c..0000000 --- a/src/arcmira/products/related/types/channels_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/products/related/types/channels_related_request_mode.py b/src/arcmira/products/related/types/channels_related_request_mode.py deleted file mode 100644 index 49a4137..0000000 --- a/src/arcmira/products/related/types/channels_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/products/related/types/channels_related_request_order.py b/src/arcmira/products/related/types/channels_related_request_order.py deleted file mode 100644 index 0f5b302..0000000 --- a/src/arcmira/products/related/types/channels_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/products/related/types/organizations_related_request_is_appearance.py b/src/arcmira/products/related/types/organizations_related_request_is_appearance.py deleted file mode 100644 index 2762d60..0000000 --- a/src/arcmira/products/related/types/organizations_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/products/related/types/organizations_related_request_mode.py b/src/arcmira/products/related/types/organizations_related_request_mode.py deleted file mode 100644 index 2bdd64b..0000000 --- a/src/arcmira/products/related/types/organizations_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/products/related/types/organizations_related_request_order.py b/src/arcmira/products/related/types/organizations_related_request_order.py deleted file mode 100644 index 4c5dde2..0000000 --- a/src/arcmira/products/related/types/organizations_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/products/related/types/people_related_request_is_appearance.py b/src/arcmira/products/related/types/people_related_request_is_appearance.py deleted file mode 100644 index af591fc..0000000 --- a/src/arcmira/products/related/types/people_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/products/related/types/people_related_request_mode.py b/src/arcmira/products/related/types/people_related_request_mode.py deleted file mode 100644 index 9d9b51c..0000000 --- a/src/arcmira/products/related/types/people_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/products/related/types/people_related_request_order.py b/src/arcmira/products/related/types/people_related_request_order.py deleted file mode 100644 index a0d19ad..0000000 --- a/src/arcmira/products/related/types/people_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/products/related/types/products_related_request_is_appearance.py b/src/arcmira/products/related/types/products_related_request_is_appearance.py deleted file mode 100644 index a6aba02..0000000 --- a/src/arcmira/products/related/types/products_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/products/related/types/products_related_request_mode.py b/src/arcmira/products/related/types/products_related_request_mode.py deleted file mode 100644 index 9046641..0000000 --- a/src/arcmira/products/related/types/products_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/products/related/types/products_related_request_order.py b/src/arcmira/products/related/types/products_related_request_order.py deleted file mode 100644 index 3e5eb9f..0000000 --- a/src/arcmira/products/related/types/products_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/products/related/types/topics_related_request_is_appearance.py b/src/arcmira/products/related/types/topics_related_request_is_appearance.py deleted file mode 100644 index 2b7c65e..0000000 --- a/src/arcmira/products/related/types/topics_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/products/related/types/topics_related_request_mode.py b/src/arcmira/products/related/types/topics_related_request_mode.py deleted file mode 100644 index 090ee69..0000000 --- a/src/arcmira/products/related/types/topics_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/products/related/types/topics_related_request_order.py b/src/arcmira/products/related/types/topics_related_request_order.py deleted file mode 100644 index 56c645a..0000000 --- a/src/arcmira/products/related/types/topics_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/recommendations/__init__.py b/src/arcmira/recommendations/__init__.py index 539136d..808f5b1 100644 --- a/src/arcmira/recommendations/__init__.py +++ b/src/arcmira/recommendations/__init__.py @@ -6,11 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import ListRecommendationsRequestEntityType, ListRecommendationsRequestMentionClass -_dynamic_imports: typing.Dict[str, str] = { - "ListRecommendationsRequestEntityType": ".types", - "ListRecommendationsRequestMentionClass": ".types", -} + from .types import ListRecommendationsRequestClass +_dynamic_imports: typing.Dict[str, str] = {"ListRecommendationsRequestClass": ".types"} def __getattr__(attr_name: str) -> typing.Any: @@ -34,4 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ListRecommendationsRequestEntityType", "ListRecommendationsRequestMentionClass"] +__all__ = ["ListRecommendationsRequestClass"] diff --git a/src/arcmira/recommendations/client.py b/src/arcmira/recommendations/client.py index ea369fd..754e86f 100644 --- a/src/arcmira/recommendations/client.py +++ b/src/arcmira/recommendations/client.py @@ -8,8 +8,7 @@ from ..types.recommendation import Recommendation from ..types.recommendation_list_response import RecommendationListResponse from .raw_client import AsyncRawRecommendationsClient, RawRecommendationsClient -from .types.list_recommendations_request_entity_type import ListRecommendationsRequestEntityType -from .types.list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass +from .types.list_recommendations_request_class import ListRecommendationsRequestClass class RecommendationsClient: @@ -30,47 +29,43 @@ def with_raw_response(self) -> RawRecommendationsClient: def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListRecommendationsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, + class_: typing.Optional[ListRecommendationsRequestClass] = None, min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, include_disputed: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[Recommendation, RecommendationListResponse]: """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. + Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds). Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListRecommendationsRequestEntityType] - channel_id : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] + class_ : typing.Optional[ListRecommendationsRequestClass] + The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). min_confidence : typing.Optional[float] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. include_disputed : typing.Optional[bool] @@ -89,7 +84,9 @@ def list( client = Arcmira( api_key="YOUR_API_KEY", ) - response = client.recommendations.list() + response = client.recommendations.list( + entity_id="entity_id", + ) for item in response: yield item # alternatively, you can paginate page-by-page @@ -97,17 +94,14 @@ def list( yield page """ return self._raw_client.list( + entity_id=entity_id, limit=limit, cursor=cursor, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, + class_=class_, min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, + after=after, + before=before, include_disputed=include_disputed, request_options=request_options, ) @@ -131,47 +125,43 @@ def with_raw_response(self) -> AsyncRawRecommendationsClient: async def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListRecommendationsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, + class_: typing.Optional[ListRecommendationsRequestClass] = None, min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, include_disputed: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[Recommendation, RecommendationListResponse]: """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. + Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds). Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListRecommendationsRequestEntityType] - channel_id : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] + class_ : typing.Optional[ListRecommendationsRequestClass] + The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). min_confidence : typing.Optional[float] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. include_disputed : typing.Optional[bool] @@ -195,7 +185,9 @@ async def list( async def main() -> None: - response = await client.recommendations.list() + response = await client.recommendations.list( + entity_id="entity_id", + ) async for item in response: yield item @@ -207,17 +199,14 @@ async def main() -> None: asyncio.run(main()) """ return await self._raw_client.list( + entity_id=entity_id, limit=limit, cursor=cursor, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, + class_=class_, min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, + after=after, + before=before, include_disputed=include_disputed, request_options=request_options, ) diff --git a/src/arcmira/recommendations/raw_client.py b/src/arcmira/recommendations/raw_client.py index ef6348b..85bb569 100644 --- a/src/arcmira/recommendations/raw_client.py +++ b/src/arcmira/recommendations/raw_client.py @@ -19,8 +19,7 @@ from ..types.error import Error from ..types.recommendation import Recommendation from ..types.recommendation_list_response import RecommendationListResponse -from .types.list_recommendations_request_entity_type import ListRecommendationsRequestEntityType -from .types.list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass +from .types.list_recommendations_request_class import ListRecommendationsRequestClass from pydantic import ValidationError @@ -31,47 +30,43 @@ def __init__(self, *, client_wrapper: SyncClientWrapper): def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListRecommendationsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, + class_: typing.Optional[ListRecommendationsRequestClass] = None, min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, include_disputed: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> SyncPager[Recommendation, RecommendationListResponse]: """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. + Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds). Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListRecommendationsRequestEntityType] - channel_id : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] + class_ : typing.Optional[ListRecommendationsRequestClass] + The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). min_confidence : typing.Optional[float] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. include_disputed : typing.Optional[bool] @@ -90,14 +85,11 @@ def list( "limit": limit, "cursor": cursor, "entity_id": entity_id, - "entity_name": entity_name, - "entity_type": entity_type, "channel_id": channel_id, - "channel_name": channel_name, - "mention_class": mention_class, + "class": class_, "min_confidence": min_confidence, - "date_from": date_from, - "date_to": date_to, + "after": after, + "before": before, "include_disputed": include_disputed, }, request_options=request_options, @@ -111,21 +103,18 @@ def list( object_=_response.json(), ), ) - _items = _parsed_response.data + _items = _parsed_response.recommendations _parsed_next = _parsed_response.next_cursor _has_next = _parsed_next is not None and _parsed_next != "" _get_next = lambda: self.list( + entity_id=entity_id, limit=limit, cursor=_parsed_next, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, + class_=class_, min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, + after=after, + before=before, include_disputed=include_disputed, request_options=request_options, ) @@ -224,47 +213,43 @@ def __init__(self, *, client_wrapper: AsyncClientWrapper): async def list( self, *, + entity_id: str, limit: typing.Optional[int] = None, cursor: typing.Optional[str] = None, - entity_id: typing.Optional[str] = None, - entity_name: typing.Optional[str] = None, - entity_type: typing.Optional[ListRecommendationsRequestEntityType] = None, channel_id: typing.Optional[str] = None, - channel_name: typing.Optional[str] = None, - mention_class: typing.Optional[ListRecommendationsRequestMentionClass] = None, + class_: typing.Optional[ListRecommendationsRequestClass] = None, min_confidence: typing.Optional[float] = None, - date_from: typing.Optional[str] = None, - date_to: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, include_disputed: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncPager[Recommendation, RecommendationListResponse]: """ - Cursor-paginated commercial mentions (ad reads, endorsements, neutral mentions) filtered by entity (entity_id or entity_name is required), channel, mention_class, confidence, and date range. The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Read timestamps from start_seconds / end_seconds (integer seconds); the MM:SS string fields are deprecated. + Cursor-paginated commercial mentions (sponsored, organic and neutral mentions) filtered by entity (entity_id is required; resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter), channel (channel_id), class, confidence, and publication window [after, before). The signed continuation binds the route, filters, caller and visibility; invalid or old cursors return invalid_cursor. A first-page ID fence excludes later insertions, including old-date backfills. Edits and deletions to existing rows remain live. Requires a Pro+ plan. Positions are start_seconds and end_seconds (integer seconds). Parameters ---------- + entity_id : str + The entity, as an id like ent_14. Required. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + limit : typing.Optional[int] cursor : typing.Optional[str] Signed continuation from next_cursor. Bound to this route, normalized query, caller and visibility; invalid or old tokens return invalid_cursor. - entity_id : typing.Optional[str] - - entity_name : typing.Optional[str] - - entity_type : typing.Optional[ListRecommendationsRequestEntityType] - channel_id : typing.Optional[str] + Only media from this YouTube channel id (UC plus 22 characters). Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - channel_name : typing.Optional[str] - - mention_class : typing.Optional[ListRecommendationsRequestMentionClass] + class_ : typing.Optional[ListRecommendationsRequestClass] + The commercial class to return. Omit for all three. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). min_confidence : typing.Optional[float] - date_from : typing.Optional[str] + after : typing.Optional[str] + Only media published at or after this instant. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - date_to : typing.Optional[str] + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. include_disputed : typing.Optional[bool] @@ -283,14 +268,11 @@ async def list( "limit": limit, "cursor": cursor, "entity_id": entity_id, - "entity_name": entity_name, - "entity_type": entity_type, "channel_id": channel_id, - "channel_name": channel_name, - "mention_class": mention_class, + "class": class_, "min_confidence": min_confidence, - "date_from": date_from, - "date_to": date_to, + "after": after, + "before": before, "include_disputed": include_disputed, }, request_options=request_options, @@ -304,23 +286,20 @@ async def list( object_=_response.json(), ), ) - _items = _parsed_response.data + _items = _parsed_response.recommendations _parsed_next = _parsed_response.next_cursor _has_next = _parsed_next is not None and _parsed_next != "" async def _get_next(): return await self.list( + entity_id=entity_id, limit=limit, cursor=_parsed_next, - entity_id=entity_id, - entity_name=entity_name, - entity_type=entity_type, channel_id=channel_id, - channel_name=channel_name, - mention_class=mention_class, + class_=class_, min_confidence=min_confidence, - date_from=date_from, - date_to=date_to, + after=after, + before=before, include_disputed=include_disputed, request_options=request_options, ) diff --git a/src/arcmira/recommendations/types/__init__.py b/src/arcmira/recommendations/types/__init__.py index 8585260..15ed287 100644 --- a/src/arcmira/recommendations/types/__init__.py +++ b/src/arcmira/recommendations/types/__init__.py @@ -6,12 +6,8 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .list_recommendations_request_entity_type import ListRecommendationsRequestEntityType - from .list_recommendations_request_mention_class import ListRecommendationsRequestMentionClass -_dynamic_imports: typing.Dict[str, str] = { - "ListRecommendationsRequestEntityType": ".list_recommendations_request_entity_type", - "ListRecommendationsRequestMentionClass": ".list_recommendations_request_mention_class", -} + from .list_recommendations_request_class import ListRecommendationsRequestClass +_dynamic_imports: typing.Dict[str, str] = {"ListRecommendationsRequestClass": ".list_recommendations_request_class"} def __getattr__(attr_name: str) -> typing.Any: @@ -35,4 +31,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["ListRecommendationsRequestEntityType", "ListRecommendationsRequestMentionClass"] +__all__ = ["ListRecommendationsRequestClass"] diff --git a/src/arcmira/recommendations/types/list_recommendations_request_class.py b/src/arcmira/recommendations/types/list_recommendations_request_class.py new file mode 100644 index 0000000..fbf0108 --- /dev/null +++ b/src/arcmira/recommendations/types/list_recommendations_request_class.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ListRecommendationsRequestClass = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/recommendations/types/list_recommendations_request_entity_type.py b/src/arcmira/recommendations/types/list_recommendations_request_entity_type.py deleted file mode 100644 index 2dc9a73..0000000 --- a/src/arcmira/recommendations/types/list_recommendations_request_entity_type.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListRecommendationsRequestEntityType = typing.Union[ - typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any -] diff --git a/src/arcmira/recommendations/types/list_recommendations_request_mention_class.py b/src/arcmira/recommendations/types/list_recommendations_request_mention_class.py deleted file mode 100644 index a106d25..0000000 --- a/src/arcmira/recommendations/types/list_recommendations_request_mention_class.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ListRecommendationsRequestMentionClass = typing.Union[ - typing.Literal["ad_read", "endorsement", "mention", "all"], typing.Any -] diff --git a/src/arcmira/team/client.py b/src/arcmira/team/client.py deleted file mode 100644 index 10786b4..0000000 --- a/src/arcmira/team/client.py +++ /dev/null @@ -1,186 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.team_members_response import TeamMembersResponse -from ..types.team_spend_response import TeamSpendResponse -from .raw_client import AsyncRawTeamClient, RawTeamClient - -if typing.TYPE_CHECKING: - from .usage_events.client import AsyncUsageEventsClient, UsageEventsClient - - -class TeamClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawTeamClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._usage_events: typing.Optional[UsageEventsClient] = None - - @property - def with_raw_response(self) -> RawTeamClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawTeamClient - """ - return self._raw_client - - def members(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamMembersResponse: - """ - Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TeamMembersResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.team.members() - """ - _response = self._raw_client.members(request_options=request_options) - return _response.data - - def spend(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamSpendResponse: - """ - Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TeamSpendResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.team.spend() - """ - _response = self._raw_client.spend(request_options=request_options) - return _response.data - - @property - def usage_events(self): - if self._usage_events is None: - from .usage_events.client import UsageEventsClient # noqa: E402 - - self._usage_events = UsageEventsClient(client_wrapper=self._client_wrapper) - return self._usage_events - - -class AsyncTeamClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawTeamClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._usage_events: typing.Optional[AsyncUsageEventsClient] = None - - @property - def with_raw_response(self) -> AsyncRawTeamClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawTeamClient - """ - return self._raw_client - - async def members(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamMembersResponse: - """ - Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TeamMembersResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.team.members() - - - asyncio.run(main()) - """ - _response = await self._raw_client.members(request_options=request_options) - return _response.data - - async def spend(self, *, request_options: typing.Optional[RequestOptions] = None) -> TeamSpendResponse: - """ - Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TeamSpendResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.team.spend() - - - asyncio.run(main()) - """ - _response = await self._raw_client.spend(request_options=request_options) - return _response.data - - @property - def usage_events(self): - if self._usage_events is None: - from .usage_events.client import AsyncUsageEventsClient # noqa: E402 - - self._usage_events = AsyncUsageEventsClient(client_wrapper=self._client_wrapper) - return self._usage_events diff --git a/src/arcmira/team/raw_client.py b/src/arcmira/team/raw_client.py deleted file mode 100644 index 20044a9..0000000 --- a/src/arcmira/team/raw_client.py +++ /dev/null @@ -1,451 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ..core.api_error import ApiError -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.http_response import AsyncHttpResponse, HttpResponse -from ..core.parse_error import ParsingError -from ..core.pydantic_utilities import parse_obj_as -from ..core.request_options import RequestOptions -from ..errors.bad_request_error import BadRequestError -from ..errors.forbidden_error import ForbiddenError -from ..errors.internal_server_error import InternalServerError -from ..errors.not_found_error import NotFoundError -from ..errors.too_many_requests_error import TooManyRequestsError -from ..errors.unauthorized_error import UnauthorizedError -from ..types.error import Error -from ..types.team_members_response import TeamMembersResponse -from ..types.team_spend_response import TeamSpendResponse -from pydantic import ValidationError - - -class RawTeamClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def members(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TeamMembersResponse]: - """ - Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[TeamMembersResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - "v1/team/members", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TeamMembersResponse, - parse_obj_as( - type_=TeamMembersResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def spend(self, *, request_options: typing.Optional[RequestOptions] = None) -> HttpResponse[TeamSpendResponse]: - """ - Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[TeamSpendResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - "v1/team/spend", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TeamSpendResponse, - parse_obj_as( - type_=TeamSpendResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawTeamClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def members( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[TeamMembersResponse]: - """ - Active members of the team the key is scoped to, with role and seat type, earliest join first. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TeamMembersResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/team/members", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TeamMembersResponse, - parse_obj_as( - type_=TeamMembersResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def spend( - self, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[TeamSpendResponse]: - """ - Account-wide rows consumed and on-demand overage spend for every active member in the current period. Totals include personal-key use and activity before joining; they are not a team-attributed invoice. Single page, no pagination. Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TeamSpendResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/team/spend", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TeamSpendResponse, - parse_obj_as( - type_=TeamSpendResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/team/usage_events/client.py b/src/arcmira/team/usage_events/client.py deleted file mode 100644 index a6af4d5..0000000 --- a/src/arcmira/team/usage_events/client.py +++ /dev/null @@ -1,143 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.team_usage_event import TeamUsageEvent -from ...types.team_usage_events_response import TeamUsageEventsResponse -from .raw_client import AsyncRawUsageEventsClient, RawUsageEventsClient - - -class UsageEventsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawUsageEventsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawUsageEventsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawUsageEventsClient - """ - return self._raw_client - - def list( - self, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - days: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[TeamUsageEvent, TeamUsageEventsResponse]: - """ - Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - limit : typing.Optional[int] - Events per page, 1 to 100. - - cursor : typing.Optional[str] - Opaque cursor from a previous page's next_cursor. - - days : typing.Optional[int] - Look-back window in days, bounded at 90. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[TeamUsageEvent, TeamUsageEventsResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.team.usage_events.list() - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.list(limit=limit, cursor=cursor, days=days, request_options=request_options) - - -class AsyncUsageEventsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawUsageEventsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawUsageEventsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawUsageEventsClient - """ - return self._raw_client - - async def list( - self, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - days: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[TeamUsageEvent, TeamUsageEventsResponse]: - """ - Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - limit : typing.Optional[int] - Events per page, 1 to 100. - - cursor : typing.Optional[str] - Opaque cursor from a previous page's next_cursor. - - days : typing.Optional[int] - Look-back window in days, bounded at 90. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[TeamUsageEvent, TeamUsageEventsResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.team.usage_events.list() - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.list(limit=limit, cursor=cursor, days=days, request_options=request_options) diff --git a/src/arcmira/team/usage_events/raw_client.py b/src/arcmira/team/usage_events/raw_client.py deleted file mode 100644 index 5512fc9..0000000 --- a/src/arcmira/team/usage_events/raw_client.py +++ /dev/null @@ -1,302 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.team_usage_event import TeamUsageEvent -from ...types.team_usage_events_response import TeamUsageEventsResponse -from pydantic import ValidationError - - -class RawUsageEventsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - days: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[TeamUsageEvent, TeamUsageEventsResponse]: - """ - Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - limit : typing.Optional[int] - Events per page, 1 to 100. - - cursor : typing.Optional[str] - Opaque cursor from a previous page's next_cursor. - - days : typing.Optional[int] - Look-back window in days, bounded at 90. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[TeamUsageEvent, TeamUsageEventsResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - "v1/team/usage-events", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "days": days, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - TeamUsageEventsResponse, - parse_obj_as( - type_=TeamUsageEventsResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list( - limit=limit, - cursor=_parsed_next, - days=days, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawUsageEventsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - days: typing.Optional[int] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[TeamUsageEvent, TeamUsageEventsResponse]: - """ - Account usage log across every active team member, including personal-key use and activity before joining. Ordered by created_at and id descending, bounded to a 90-day look-back. Continuation preserves the first page's time window and excludes subsequently inserted events, including backfills. Removed members and deleted events disappear during traversal; this is not a historical membership snapshot. Cursors expire after 24 hours and bind the team, caller, days and limit; invalid or changed-query cursors return invalid_cursor rather than restarting. The aggregated analytics chart data is not exposed on this API (Enterprise). Requires a team-scoped API key whose owner is still an active team admin. Personal keys and keys whose owner lost team authority receive 403 (team_key_required). - - Parameters - ---------- - limit : typing.Optional[int] - Events per page, 1 to 100. - - cursor : typing.Optional[str] - Opaque cursor from a previous page's next_cursor. - - days : typing.Optional[int] - Look-back window in days, bounded at 90. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[TeamUsageEvent, TeamUsageEventsResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/team/usage-events", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "days": days, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - TeamUsageEventsResponse, - parse_obj_as( - type_=TeamUsageEventsResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.data - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.list( - limit=limit, - cursor=_parsed_next, - days=days, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/topics/__init__.py b/src/arcmira/topics/__init__.py deleted file mode 100644 index c556990..0000000 --- a/src/arcmira/topics/__init__.py +++ /dev/null @@ -1,85 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from . import related - from .related import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".related", - "ChannelsRelatedRequestMode": ".related", - "ChannelsRelatedRequestOrder": ".related", - "OrganizationsRelatedRequestIsAppearance": ".related", - "OrganizationsRelatedRequestMode": ".related", - "OrganizationsRelatedRequestOrder": ".related", - "PeopleRelatedRequestIsAppearance": ".related", - "PeopleRelatedRequestMode": ".related", - "PeopleRelatedRequestOrder": ".related", - "ProductsRelatedRequestIsAppearance": ".related", - "ProductsRelatedRequestMode": ".related", - "ProductsRelatedRequestOrder": ".related", - "TopicsRelatedRequestIsAppearance": ".related", - "TopicsRelatedRequestMode": ".related", - "TopicsRelatedRequestOrder": ".related", - "related": ".related", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", - "related", -] diff --git a/src/arcmira/topics/client.py b/src/arcmira/topics/client.py deleted file mode 100644 index c5ebbcc..0000000 --- a/src/arcmira/topics/client.py +++ /dev/null @@ -1,131 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import typing - -from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ..core.request_options import RequestOptions -from ..types.topic_page_response import TopicPageResponse -from .raw_client import AsyncRawTopicsClient, RawTopicsClient - -if typing.TYPE_CHECKING: - from .related.client import AsyncRelatedClient, RelatedClient - - -class TopicsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawTopicsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[RelatedClient] = None - - @property - def with_raw_response(self) -> RawTopicsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawTopicsClient - """ - return self._raw_client - - def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> TopicPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TopicPageResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.topics.get( - slug="slug", - ) - """ - _response = self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import RelatedClient # noqa: E402 - - self._related = RelatedClient(client_wrapper=self._client_wrapper) - return self._related - - -class AsyncTopicsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawTopicsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._related: typing.Optional[AsyncRelatedClient] = None - - @property - def with_raw_response(self) -> AsyncRawTopicsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawTopicsClient - """ - return self._raw_client - - async def get(self, slug: str, *, request_options: typing.Optional[RequestOptions] = None) -> TopicPageResponse: - """ - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TopicPageResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.topics.get( - slug="slug", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.get(slug, request_options=request_options) - return _response.data - - @property - def related(self): - if self._related is None: - from .related.client import AsyncRelatedClient # noqa: E402 - - self._related = AsyncRelatedClient(client_wrapper=self._client_wrapper) - return self._related diff --git a/src/arcmira/topics/related/__init__.py b/src/arcmira/topics/related/__init__.py deleted file mode 100644 index bed85d9..0000000 --- a/src/arcmira/topics/related/__init__.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .types import ( - ChannelsRelatedRequestIsAppearance, - ChannelsRelatedRequestMode, - ChannelsRelatedRequestOrder, - OrganizationsRelatedRequestIsAppearance, - OrganizationsRelatedRequestMode, - OrganizationsRelatedRequestOrder, - PeopleRelatedRequestIsAppearance, - PeopleRelatedRequestMode, - PeopleRelatedRequestOrder, - ProductsRelatedRequestIsAppearance, - ProductsRelatedRequestMode, - ProductsRelatedRequestOrder, - TopicsRelatedRequestIsAppearance, - TopicsRelatedRequestMode, - TopicsRelatedRequestOrder, - ) -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".types", - "ChannelsRelatedRequestMode": ".types", - "ChannelsRelatedRequestOrder": ".types", - "OrganizationsRelatedRequestIsAppearance": ".types", - "OrganizationsRelatedRequestMode": ".types", - "OrganizationsRelatedRequestOrder": ".types", - "PeopleRelatedRequestIsAppearance": ".types", - "PeopleRelatedRequestMode": ".types", - "PeopleRelatedRequestOrder": ".types", - "ProductsRelatedRequestIsAppearance": ".types", - "ProductsRelatedRequestMode": ".types", - "ProductsRelatedRequestOrder": ".types", - "TopicsRelatedRequestIsAppearance": ".types", - "TopicsRelatedRequestMode": ".types", - "TopicsRelatedRequestOrder": ".types", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/topics/related/client.py b/src/arcmira/topics/related/client.py deleted file mode 100644 index 802abf3..0000000 --- a/src/arcmira/topics/related/client.py +++ /dev/null @@ -1,930 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.pagination import AsyncPager, SyncPager -from ...core.request_options import RequestOptions -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .raw_client import AsyncRawRelatedClient, RawRelatedClient -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder - - -class RelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawRelatedClient - """ - return self._raw_client - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.topics.related.topics( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.topics.related.people( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.topics.related.organizations( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.topics.related.products( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - response = client.topics.related.channels( - slug="slug", - ) - for item in response: - yield item - # alternatively, you can paginate page-by-page - for page in response.iter_pages(): - yield page - """ - return self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - -class AsyncRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawRelatedClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawRelatedClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawRelatedClient - """ - return self._raw_client - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.topics.related.topics( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.topics( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.topics.related.people( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.people( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.topics.related.organizations( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.organizations( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.topics.related.products( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.products( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - response = await client.topics.related.channels( - slug="slug", - ) - async for item in response: - yield item - - # alternatively, you can paginate page-by-page - async for page in response.iter_pages(): - yield page - - - asyncio.run(main()) - """ - return await self._raw_client.channels( - slug, - limit=limit, - cursor=cursor, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) diff --git a/src/arcmira/topics/related/raw_client.py b/src/arcmira/topics/related/raw_client.py deleted file mode 100644 index 10e1f27..0000000 --- a/src/arcmira/topics/related/raw_client.py +++ /dev/null @@ -1,1861 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.jsonable_encoder import encode_path_param -from ...core.pagination import AsyncPager, SyncPager -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.payment_required_error import PaymentRequiredError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.entity_channel_list_response import EntityChannelListResponse -from ...types.entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from ...types.entity_organization_list_response import EntityOrganizationListResponse -from ...types.entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from ...types.entity_people_list_response import EntityPeopleListResponse -from ...types.entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from ...types.entity_product_list_response import EntityProductListResponse -from ...types.entity_product_list_response_items_item import EntityProductListResponseItemsItem -from ...types.entity_topic_list_response import EntityTopicListResponse -from ...types.entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from ...types.error import Error -from .types.channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance -from .types.channels_related_request_mode import ChannelsRelatedRequestMode -from .types.channels_related_request_order import ChannelsRelatedRequestOrder -from .types.organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance -from .types.organizations_related_request_mode import OrganizationsRelatedRequestMode -from .types.organizations_related_request_order import OrganizationsRelatedRequestOrder -from .types.people_related_request_is_appearance import PeopleRelatedRequestIsAppearance -from .types.people_related_request_mode import PeopleRelatedRequestMode -from .types.people_related_request_order import PeopleRelatedRequestOrder -from .types.products_related_request_is_appearance import ProductsRelatedRequestIsAppearance -from .types.products_related_request_mode import ProductsRelatedRequestMode -from .types.products_related_request_order import ProductsRelatedRequestOrder -from .types.topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance -from .types.topics_related_request_mode import TopicsRelatedRequestMode -from .types.topics_related_request_order import TopicsRelatedRequestOrder -from pydantic import ValidationError - - -class RawRelatedClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawRelatedClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def topics( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[TopicsRelatedRequestOrder] = None, - mode: typing.Optional[TopicsRelatedRequestMode] = None, - is_appearance: typing.Optional[TopicsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse]: - """ - The topics that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[TopicsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[TopicsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[TopicsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityTopicListResponseItemsItem, EntityTopicListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/topics", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityTopicListResponse, - parse_obj_as( - type_=EntityTopicListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.topics( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def people( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[PeopleRelatedRequestOrder] = None, - mode: typing.Optional[PeopleRelatedRequestMode] = None, - is_appearance: typing.Optional[PeopleRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse]: - """ - The people that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[PeopleRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[PeopleRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[PeopleRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityPeopleListResponseItemsItem, EntityPeopleListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/people", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityPeopleListResponse, - parse_obj_as( - type_=EntityPeopleListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.people( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def organizations( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[OrganizationsRelatedRequestOrder] = None, - mode: typing.Optional[OrganizationsRelatedRequestMode] = None, - is_appearance: typing.Optional[OrganizationsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse]: - """ - The organizations that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[OrganizationsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[OrganizationsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[OrganizationsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityOrganizationListResponseItemsItem, EntityOrganizationListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/organizations", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityOrganizationListResponse, - parse_obj_as( - type_=EntityOrganizationListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.organizations( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def products( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ProductsRelatedRequestOrder] = None, - mode: typing.Optional[ProductsRelatedRequestMode] = None, - is_appearance: typing.Optional[ProductsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse]: - """ - The products that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ProductsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ProductsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ProductsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityProductListResponseItemsItem, EntityProductListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/products", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityProductListResponse, - parse_obj_as( - type_=EntityProductListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.products( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def channels( - self, - slug: str, - *, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - q: typing.Optional[str] = None, - field: typing.Optional[str] = None, - sort: typing.Optional[str] = None, - order: typing.Optional[ChannelsRelatedRequestOrder] = None, - mode: typing.Optional[ChannelsRelatedRequestMode] = None, - is_appearance: typing.Optional[ChannelsRelatedRequestIsAppearance] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse]: - """ - The channels that co-occur with this topic in indexed media, with q/field/sort/order filtering. Signed cursor pagination: rows are in items, and next_cursor (null on the last page) feeds cursor. A token binds route, filters, caller and visibility; malformed or old tokens return invalid_cursor. Aggregate and display lists are live: changed ranks or deleted rows may shift later pages. total counts every matching row and limit is the page size applied. - - Parameters - ---------- - slug : str - The entity slug: the last segment of its arcmira.com page URL, as EntityRef.slug carries it. - - limit : typing.Optional[int] - - cursor : typing.Optional[str] - Signed continuation from next_cursor. Bound to route, filters, caller and visibility; invalid or old tokens return invalid_cursor. Person appearance publication pages use a media-id insertion fence; aggregate sorts remain live. - - q : typing.Optional[str] - Substring filter over the row's text columns (e.g. video title, channel name, description). - - field : typing.Optional[str] - Restrict the q filter to one column. Default "any" (all searchable columns). - - sort : typing.Optional[str] - Sort key. Rows default to newest first; supported values vary by list (e.g. "date", "channel"). - - order : typing.Optional[ChannelsRelatedRequestOrder] - Sort direction. Default desc. - - mode : typing.Optional[ChannelsRelatedRequestMode] - Person relationship lens: guest appearances or inbound mentions. - - is_appearance : typing.Optional[ChannelsRelatedRequestIsAppearance] - For person appearances, true lists guest episodes and false lists inbound mentions. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncPager[EntityChannelListResponseItemsItem, EntityChannelListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/topics/{encode_path_param(slug)}/channels", - method="GET", - params={ - "limit": limit, - "cursor": cursor, - "q": q, - "field": field, - "sort": sort, - "order": order, - "mode": mode, - "is_appearance": is_appearance, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - EntityChannelListResponse, - parse_obj_as( - type_=EntityChannelListResponse, # type: ignore - object_=_response.json(), - ), - ) - _items = _parsed_response.items - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - - async def _get_next(): - return await self.channels( - slug, - limit=limit, - cursor=_parsed_next, - q=q, - field=field, - sort=sort, - order=order, - mode=mode, - is_appearance=is_appearance, - request_options=request_options, - ) - - return AsyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/topics/related/types/__init__.py b/src/arcmira/topics/related/types/__init__.py deleted file mode 100644 index 1f3a8a7..0000000 --- a/src/arcmira/topics/related/types/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file - -import typing -from importlib import import_module - -if typing.TYPE_CHECKING: - from .channels_related_request_is_appearance import ChannelsRelatedRequestIsAppearance - from .channels_related_request_mode import ChannelsRelatedRequestMode - from .channels_related_request_order import ChannelsRelatedRequestOrder - from .organizations_related_request_is_appearance import OrganizationsRelatedRequestIsAppearance - from .organizations_related_request_mode import OrganizationsRelatedRequestMode - from .organizations_related_request_order import OrganizationsRelatedRequestOrder - from .people_related_request_is_appearance import PeopleRelatedRequestIsAppearance - from .people_related_request_mode import PeopleRelatedRequestMode - from .people_related_request_order import PeopleRelatedRequestOrder - from .products_related_request_is_appearance import ProductsRelatedRequestIsAppearance - from .products_related_request_mode import ProductsRelatedRequestMode - from .products_related_request_order import ProductsRelatedRequestOrder - from .topics_related_request_is_appearance import TopicsRelatedRequestIsAppearance - from .topics_related_request_mode import TopicsRelatedRequestMode - from .topics_related_request_order import TopicsRelatedRequestOrder -_dynamic_imports: typing.Dict[str, str] = { - "ChannelsRelatedRequestIsAppearance": ".channels_related_request_is_appearance", - "ChannelsRelatedRequestMode": ".channels_related_request_mode", - "ChannelsRelatedRequestOrder": ".channels_related_request_order", - "OrganizationsRelatedRequestIsAppearance": ".organizations_related_request_is_appearance", - "OrganizationsRelatedRequestMode": ".organizations_related_request_mode", - "OrganizationsRelatedRequestOrder": ".organizations_related_request_order", - "PeopleRelatedRequestIsAppearance": ".people_related_request_is_appearance", - "PeopleRelatedRequestMode": ".people_related_request_mode", - "PeopleRelatedRequestOrder": ".people_related_request_order", - "ProductsRelatedRequestIsAppearance": ".products_related_request_is_appearance", - "ProductsRelatedRequestMode": ".products_related_request_mode", - "ProductsRelatedRequestOrder": ".products_related_request_order", - "TopicsRelatedRequestIsAppearance": ".topics_related_request_is_appearance", - "TopicsRelatedRequestMode": ".topics_related_request_mode", - "TopicsRelatedRequestOrder": ".topics_related_request_order", -} - - -def __getattr__(attr_name: str) -> typing.Any: - module_name = _dynamic_imports.get(attr_name) - if module_name is None: - raise AttributeError(f"No {attr_name} found in _dynamic_imports for module name -> {__name__}") - try: - module = import_module(module_name, __package__) - if module_name == f".{attr_name}": - return module - else: - return getattr(module, attr_name) - except ImportError as e: - raise ImportError(f"Failed to import {attr_name} from {module_name}: {e}") from e - except AttributeError as e: - raise AttributeError(f"Failed to get {attr_name} from {module_name}: {e}") from e - - -def __dir__(): - lazy_attrs = list(_dynamic_imports.keys()) - return sorted(lazy_attrs) - - -__all__ = [ - "ChannelsRelatedRequestIsAppearance", - "ChannelsRelatedRequestMode", - "ChannelsRelatedRequestOrder", - "OrganizationsRelatedRequestIsAppearance", - "OrganizationsRelatedRequestMode", - "OrganizationsRelatedRequestOrder", - "PeopleRelatedRequestIsAppearance", - "PeopleRelatedRequestMode", - "PeopleRelatedRequestOrder", - "ProductsRelatedRequestIsAppearance", - "ProductsRelatedRequestMode", - "ProductsRelatedRequestOrder", - "TopicsRelatedRequestIsAppearance", - "TopicsRelatedRequestMode", - "TopicsRelatedRequestOrder", -] diff --git a/src/arcmira/topics/related/types/channels_related_request_is_appearance.py b/src/arcmira/topics/related/types/channels_related_request_is_appearance.py deleted file mode 100644 index e21cd2c..0000000 --- a/src/arcmira/topics/related/types/channels_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/topics/related/types/channels_related_request_mode.py b/src/arcmira/topics/related/types/channels_related_request_mode.py deleted file mode 100644 index 49a4137..0000000 --- a/src/arcmira/topics/related/types/channels_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/topics/related/types/channels_related_request_order.py b/src/arcmira/topics/related/types/channels_related_request_order.py deleted file mode 100644 index 0f5b302..0000000 --- a/src/arcmira/topics/related/types/channels_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/topics/related/types/organizations_related_request_is_appearance.py b/src/arcmira/topics/related/types/organizations_related_request_is_appearance.py deleted file mode 100644 index 2762d60..0000000 --- a/src/arcmira/topics/related/types/organizations_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/topics/related/types/organizations_related_request_mode.py b/src/arcmira/topics/related/types/organizations_related_request_mode.py deleted file mode 100644 index 2bdd64b..0000000 --- a/src/arcmira/topics/related/types/organizations_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/topics/related/types/organizations_related_request_order.py b/src/arcmira/topics/related/types/organizations_related_request_order.py deleted file mode 100644 index 4c5dde2..0000000 --- a/src/arcmira/topics/related/types/organizations_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/topics/related/types/people_related_request_is_appearance.py b/src/arcmira/topics/related/types/people_related_request_is_appearance.py deleted file mode 100644 index af591fc..0000000 --- a/src/arcmira/topics/related/types/people_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/topics/related/types/people_related_request_mode.py b/src/arcmira/topics/related/types/people_related_request_mode.py deleted file mode 100644 index 9d9b51c..0000000 --- a/src/arcmira/topics/related/types/people_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/topics/related/types/people_related_request_order.py b/src/arcmira/topics/related/types/people_related_request_order.py deleted file mode 100644 index a0d19ad..0000000 --- a/src/arcmira/topics/related/types/people_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PeopleRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/topics/related/types/products_related_request_is_appearance.py b/src/arcmira/topics/related/types/products_related_request_is_appearance.py deleted file mode 100644 index a6aba02..0000000 --- a/src/arcmira/topics/related/types/products_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/topics/related/types/products_related_request_mode.py b/src/arcmira/topics/related/types/products_related_request_mode.py deleted file mode 100644 index 9046641..0000000 --- a/src/arcmira/topics/related/types/products_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/topics/related/types/products_related_request_order.py b/src/arcmira/topics/related/types/products_related_request_order.py deleted file mode 100644 index 3e5eb9f..0000000 --- a/src/arcmira/topics/related/types/products_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/topics/related/types/topics_related_request_is_appearance.py b/src/arcmira/topics/related/types/topics_related_request_is_appearance.py deleted file mode 100644 index 2b7c65e..0000000 --- a/src/arcmira/topics/related/types/topics_related_request_is_appearance.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestIsAppearance = typing.Union[typing.Literal["0", "1", "true", "false"], typing.Any] diff --git a/src/arcmira/topics/related/types/topics_related_request_mode.py b/src/arcmira/topics/related/types/topics_related_request_mode.py deleted file mode 100644 index 090ee69..0000000 --- a/src/arcmira/topics/related/types/topics_related_request_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/topics/related/types/topics_related_request_order.py b/src/arcmira/topics/related/types/topics_related_request_order.py deleted file mode 100644 index 56c645a..0000000 --- a/src/arcmira/topics/related/types/topics_related_request_order.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicsRelatedRequestOrder = typing.Union[typing.Literal["asc", "desc"], typing.Any] diff --git a/src/arcmira/trackers/client.py b/src/arcmira/trackers/client.py index 3cd8b5a..1c06c88 100644 --- a/src/arcmira/trackers/client.py +++ b/src/arcmira/trackers/client.py @@ -81,15 +81,15 @@ def create( request_options: typing.Optional[RequestOptions] = None, ) -> TrackerMutationResponse: """ - Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId. + Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. Parameters ---------- entity_name : str - The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType - Entity type of the tracked entity. Required on create. + Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. @@ -138,7 +138,7 @@ def create( ) client.trackers.create( idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_name="entityName", + entity_name="entity_name", entity_type="person", ) """ @@ -218,7 +218,7 @@ def update( request_options: typing.Optional[RequestOptions] = None, ) -> TrackerMutationResponse: """ - Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity. + Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity. Parameters ---------- @@ -373,15 +373,15 @@ async def create( request_options: typing.Optional[RequestOptions] = None, ) -> TrackerMutationResponse: """ - Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId. + Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. Parameters ---------- entity_name : str - The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType - Entity type of the tracked entity. Required on create. + Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. @@ -435,7 +435,7 @@ async def create( async def main() -> None: await client.trackers.create( idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_name="entityName", + entity_name="entity_name", entity_type="person", ) @@ -526,7 +526,7 @@ async def update( request_options: typing.Optional[RequestOptions] = None, ) -> TrackerMutationResponse: """ - Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity. + Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity. Parameters ---------- diff --git a/src/arcmira/trackers/raw_client.py b/src/arcmira/trackers/raw_client.py index f5698fe..6f7f2e7 100644 --- a/src/arcmira/trackers/raw_client.py +++ b/src/arcmira/trackers/raw_client.py @@ -156,15 +156,15 @@ def create( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[TrackerMutationResponse]: """ - Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId. + Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. Parameters ---------- entity_name : str - The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType - Entity type of the tracked entity. Required on create. + Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. @@ -208,16 +208,16 @@ def create( "v1/trackers", method="POST", json={ - "entityName": entity_name, - "entityType": entity_type, - "displayName": display_name, - "notifyEmail": notify_email, - "notifyWebhook": notify_webhook, - "notifySlack": notify_slack, - "webhookUrl": webhook_url, - "slackChannelId": slack_channel_id, - "slackIntegrationId": slack_integration_id, - "personMatchMode": person_match_mode, + "entity_name": entity_name, + "entity_type": entity_type, + "display_name": display_name, + "notify_email": notify_email, + "notify_webhook": notify_webhook, + "notify_slack": notify_slack, + "webhook_url": webhook_url, + "slack_channel_id": slack_channel_id, + "slack_integration_id": slack_integration_id, + "person_match_mode": person_match_mode, "filters": filters, }, headers={ @@ -471,7 +471,7 @@ def update( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[TrackerMutationResponse]: """ - Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity. + Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity. Parameters ---------- @@ -523,14 +523,14 @@ def update( f"v1/trackers/{encode_path_param(id)}", method="PATCH", json={ - "displayName": display_name, - "notifyEmail": notify_email, - "notifyWebhook": notify_webhook, - "notifySlack": notify_slack, - "webhookUrl": webhook_url, - "slackChannelId": slack_channel_id, - "slackIntegrationId": slack_integration_id, - "personMatchMode": person_match_mode, + "display_name": display_name, + "notify_email": notify_email, + "notify_webhook": notify_webhook, + "notify_slack": notify_slack, + "webhook_url": webhook_url, + "slack_channel_id": slack_channel_id, + "slack_integration_id": slack_integration_id, + "person_match_mode": person_match_mode, "filters": filters, "paused": paused, }, @@ -766,15 +766,15 @@ async def create( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[TrackerMutationResponse]: """ - Creates a standalone tracker watching one entity (name + type, resolved with the same entity resolution Search uses). Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 with the existingId. + Creates a standalone tracker watching one exact name and type, matched case-insensitively against entities in newly analyzed media, so a tracker can exist before the entity is indexed. To follow an entity you already have an id for, use POST /v1/monitors/{id}/entities. A channel is followed by its YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required. Attach it to a monitor afterwards via POST /v1/monitors/{id}/trackers. Creating a duplicate (same entity name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. Parameters ---------- entity_name : str - The entity name to resolve and watch. Required on create. Creating a duplicate (same name + type) returns 409 with the existingId. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType - Entity type of the tracked entity. Required on create. + Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. @@ -818,16 +818,16 @@ async def create( "v1/trackers", method="POST", json={ - "entityName": entity_name, - "entityType": entity_type, - "displayName": display_name, - "notifyEmail": notify_email, - "notifyWebhook": notify_webhook, - "notifySlack": notify_slack, - "webhookUrl": webhook_url, - "slackChannelId": slack_channel_id, - "slackIntegrationId": slack_integration_id, - "personMatchMode": person_match_mode, + "entity_name": entity_name, + "entity_type": entity_type, + "display_name": display_name, + "notify_email": notify_email, + "notify_webhook": notify_webhook, + "notify_slack": notify_slack, + "webhook_url": webhook_url, + "slack_channel_id": slack_channel_id, + "slack_integration_id": slack_integration_id, + "person_match_mode": person_match_mode, "filters": filters, }, headers={ @@ -1081,7 +1081,7 @@ async def update( request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[TrackerMutationResponse]: """ - Partial update: send only the fields to change. The tracked entity itself (entityName/entityType) is immutable; delete and recreate to watch a different entity. + Partial update: send only the fields to change. The tracked entity itself (entity_name/entity_type) is immutable; delete and recreate to watch a different entity. Parameters ---------- @@ -1133,14 +1133,14 @@ async def update( f"v1/trackers/{encode_path_param(id)}", method="PATCH", json={ - "displayName": display_name, - "notifyEmail": notify_email, - "notifyWebhook": notify_webhook, - "notifySlack": notify_slack, - "webhookUrl": webhook_url, - "slackChannelId": slack_channel_id, - "slackIntegrationId": slack_integration_id, - "personMatchMode": person_match_mode, + "display_name": display_name, + "notify_email": notify_email, + "notify_webhook": notify_webhook, + "notify_slack": notify_slack, + "webhook_url": webhook_url, + "slack_channel_id": slack_channel_id, + "slack_integration_id": slack_integration_id, + "person_match_mode": person_match_mode, "filters": filters, "paused": paused, }, diff --git a/src/arcmira/transcripts/__init__.py b/src/arcmira/transcripts/__init__.py index cc669be..4f556e2 100644 --- a/src/arcmira/transcripts/__init__.py +++ b/src/arcmira/transcripts/__init__.py @@ -7,13 +7,9 @@ if typing.TYPE_CHECKING: from .types import GetTranscriptsRequestQuality, SearchTranscriptsRequestSource - from . import edits, merges, speakers _dynamic_imports: typing.Dict[str, str] = { "GetTranscriptsRequestQuality": ".types", "SearchTranscriptsRequestSource": ".types", - "edits": ".edits", - "merges": ".merges", - "speakers": ".speakers", } @@ -38,4 +34,4 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["GetTranscriptsRequestQuality", "SearchTranscriptsRequestSource", "edits", "merges", "speakers"] +__all__ = ["GetTranscriptsRequestQuality", "SearchTranscriptsRequestSource"] diff --git a/src/arcmira/transcripts/client.py b/src/arcmira/transcripts/client.py index 17d8e9c..296c709 100644 --- a/src/arcmira/transcripts/client.py +++ b/src/arcmira/transcripts/client.py @@ -1,40 +1,23 @@ # This file was auto-generated by Fern from our API Definition. -from __future__ import annotations - import typing from ..core.client_wrapper import AsyncClientWrapper, SyncClientWrapper from ..core.pagination import AsyncPager, SyncPager from ..core.request_options import RequestOptions -from ..types.transcript_job import TranscriptJob from ..types.transcript_purchase_quote import TranscriptPurchaseQuote from ..types.transcript_request_list_response import TranscriptRequestListResponse from ..types.transcript_request_list_response_requests_item import TranscriptRequestListResponseRequestsItem -from ..types.transcript_request_submit_response import TranscriptRequestSubmitResponse from ..types.transcript_result import TranscriptResult from ..types.transcript_search_response import TranscriptSearchResponse -from ..types.video_captions_response import VideoCaptionsResponse -from .prepare import AsyncPrepareAndWait, PrepareAndWait from .raw_client import AsyncRawTranscriptsClient, RawTranscriptsClient from .types.get_transcripts_request_quality import GetTranscriptsRequestQuality from .types.search_transcripts_request_source import SearchTranscriptsRequestSource -if typing.TYPE_CHECKING: - from .edits.client import AsyncEditsClient, EditsClient - from .merges.client import AsyncMergesClient, MergesClient - from .speakers.client import AsyncSpeakersClient, SpeakersClient -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - -class TranscriptsClient(PrepareAndWait): +class TranscriptsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): self._raw_client = RawTranscriptsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._edits: typing.Optional[EditsClient] = None - self._speakers: typing.Optional[SpeakersClient] = None - self._merges: typing.Optional[MergesClient] = None @property def with_raw_response(self) -> RawTranscriptsClient: @@ -57,14 +40,14 @@ def search( about: typing.Optional[str] = None, by: typing.Optional[str] = None, kind: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, source: typing.Optional[SearchTranscriptsRequestSource] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptSearchResponse: """ - Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -87,13 +70,13 @@ def search( Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. kind : typing.Optional[str] - Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk. + Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk. - published_after : typing.Optional[str] - ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. source : typing.Optional[SearchTranscriptsRequestSource] Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. @@ -128,8 +111,8 @@ def search( about=about, by=by, kind=kind, - published_after=published_after, - published_before=published_before, + after=after, + before=before, source=source, limit=limit, request_options=request_options, @@ -149,7 +132,7 @@ def get( request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptResult: """ - Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. Parameters ---------- @@ -157,7 +140,7 @@ def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -180,7 +163,7 @@ def get( Returns ------- TranscriptResult - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state preparation_required: Premium is not owned yet; the quote and the POST that prepares it. + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). Examples -------- @@ -209,7 +192,7 @@ def quote( self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> TranscriptPurchaseQuote: """ - Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. + Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. Parameters ---------- @@ -238,39 +221,6 @@ def quote( _response = self._raw_client.quote(video_id, request_options=request_options) return _response.data - def captions( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> VideoCaptionsResponse: - """ - Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoCaptionsResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.captions( - video_id="video_id", - ) - """ - _response = self._raw_client.captions(video_id, request_options=request_options) - return _response.data - def list_requests( self, *, @@ -319,128 +269,10 @@ def list_requests( video_id=video_id, limit=limit, cursor=cursor, request_options=request_options ) - def request( - self, - *, - idempotency_key: typing.Optional[str] = None, - video_id: typing.Optional[str] = OMIT, - url: typing.Optional[str] = OMIT, - max_rows: typing.Optional[int] = OMIT, - max_on_demand_cents: typing.Optional[int] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> TranscriptRequestSubmitResponse: - """ - Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it. - - Parameters - ---------- - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - video_id : typing.Optional[str] - YouTube video id (11 characters). Either video_id or url is required. - - url : typing.Optional[str] - A YouTube watch/short/live URL. Either video_id or url is required. - - max_rows : typing.Optional[int] - Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0. - - max_on_demand_cents : typing.Optional[int] - Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptRequestSubmitResponse - job.state is ready, failed or refunded: the transcript is servable, or the purchase ended without one. - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.request( - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - ) - """ - _response = self._raw_client.request( - idempotency_key=idempotency_key, - video_id=video_id, - url=url, - max_rows=max_rows, - max_on_demand_cents=max_on_demand_cents, - request_options=request_options, - ) - return _response.data - - def status(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> TranscriptJob: - """ - Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up. - - Parameters - ---------- - id : str - Transcription request id, the UUID POST /v1/transcriptions returned. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptJob - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.status( - id="id", - ) - """ - _response = self._raw_client.status(id, request_options=request_options) - return _response.data - - @property - def edits(self): - if self._edits is None: - from .edits.client import EditsClient # noqa: E402 - - self._edits = EditsClient(client_wrapper=self._client_wrapper) - return self._edits - - @property - def speakers(self): - if self._speakers is None: - from .speakers.client import SpeakersClient # noqa: E402 - - self._speakers = SpeakersClient(client_wrapper=self._client_wrapper) - return self._speakers - - @property - def merges(self): - if self._merges is None: - from .merges.client import MergesClient # noqa: E402 - - self._merges = MergesClient(client_wrapper=self._client_wrapper) - return self._merges - -class AsyncTranscriptsClient(AsyncPrepareAndWait): +class AsyncTranscriptsClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): self._raw_client = AsyncRawTranscriptsClient(client_wrapper=client_wrapper) - self._client_wrapper = client_wrapper - self._edits: typing.Optional[AsyncEditsClient] = None - self._speakers: typing.Optional[AsyncSpeakersClient] = None - self._merges: typing.Optional[AsyncMergesClient] = None @property def with_raw_response(self) -> AsyncRawTranscriptsClient: @@ -463,14 +295,14 @@ async def search( about: typing.Optional[str] = None, by: typing.Optional[str] = None, kind: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, source: typing.Optional[SearchTranscriptsRequestSource] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptSearchResponse: """ - Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -493,13 +325,13 @@ async def search( Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. kind : typing.Optional[str] - Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk. + Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk. - published_after : typing.Optional[str] - ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. source : typing.Optional[SearchTranscriptsRequestSource] Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. @@ -542,8 +374,8 @@ async def main() -> None: about=about, by=by, kind=kind, - published_after=published_after, - published_before=published_before, + after=after, + before=before, source=source, limit=limit, request_options=request_options, @@ -563,7 +395,7 @@ async def get( request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptResult: """ - Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. Parameters ---------- @@ -571,7 +403,7 @@ async def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -594,7 +426,7 @@ async def get( Returns ------- TranscriptResult - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state preparation_required: Premium is not owned yet; the quote and the POST that prepares it. + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). Examples -------- @@ -631,7 +463,7 @@ async def quote( self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> TranscriptPurchaseQuote: """ - Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. + Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. Parameters ---------- @@ -668,47 +500,6 @@ async def main() -> None: _response = await self._raw_client.quote(video_id, request_options=request_options) return _response.data - async def captions( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> VideoCaptionsResponse: - """ - Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoCaptionsResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.captions( - video_id="video_id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.captions(video_id, request_options=request_options) - return _response.data - async def list_requests( self, *, @@ -765,133 +556,3 @@ async def main() -> None: return await self._raw_client.list_requests( video_id=video_id, limit=limit, cursor=cursor, request_options=request_options ) - - async def request( - self, - *, - idempotency_key: typing.Optional[str] = None, - video_id: typing.Optional[str] = OMIT, - url: typing.Optional[str] = OMIT, - max_rows: typing.Optional[int] = OMIT, - max_on_demand_cents: typing.Optional[int] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> TranscriptRequestSubmitResponse: - """ - Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it. - - Parameters - ---------- - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - video_id : typing.Optional[str] - YouTube video id (11 characters). Either video_id or url is required. - - url : typing.Optional[str] - A YouTube watch/short/live URL. Either video_id or url is required. - - max_rows : typing.Optional[int] - Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0. - - max_on_demand_cents : typing.Optional[int] - Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptRequestSubmitResponse - job.state is ready, failed or refunded: the transcript is servable, or the purchase ended without one. - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.request( - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.request( - idempotency_key=idempotency_key, - video_id=video_id, - url=url, - max_rows=max_rows, - max_on_demand_cents=max_on_demand_cents, - request_options=request_options, - ) - return _response.data - - async def status(self, id: str, *, request_options: typing.Optional[RequestOptions] = None) -> TranscriptJob: - """ - Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up. - - Parameters - ---------- - id : str - Transcription request id, the UUID POST /v1/transcriptions returned. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptJob - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.status( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.status(id, request_options=request_options) - return _response.data - - @property - def edits(self): - if self._edits is None: - from .edits.client import AsyncEditsClient # noqa: E402 - - self._edits = AsyncEditsClient(client_wrapper=self._client_wrapper) - return self._edits - - @property - def speakers(self): - if self._speakers is None: - from .speakers.client import AsyncSpeakersClient # noqa: E402 - - self._speakers = AsyncSpeakersClient(client_wrapper=self._client_wrapper) - return self._speakers - - @property - def merges(self): - if self._merges is None: - from .merges.client import AsyncMergesClient # noqa: E402 - - self._merges = AsyncMergesClient(client_wrapper=self._client_wrapper) - return self._merges diff --git a/src/arcmira/transcripts/edits/__init__.py b/src/arcmira/transcripts/edits/__init__.py deleted file mode 100644 index dadae70..0000000 --- a/src/arcmira/transcripts/edits/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file diff --git a/src/arcmira/transcripts/edits/client.py b/src/arcmira/transcripts/edits/client.py deleted file mode 100644 index cdac5a6..0000000 --- a/src/arcmira/transcripts/edits/client.py +++ /dev/null @@ -1,262 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.request_options import RequestOptions -from ...types.transcript_edit_submitted_response import TranscriptEditSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from .raw_client import AsyncRawEditsClient, RawEditsClient - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class EditsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawEditsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawEditsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawEditsClient - """ - return self._raw_client - - def submit( - self, - video_id: str, - *, - segment_index: int, - original_text: str, - corrected_text: str, - idempotency_key: typing.Optional[str] = None, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> TranscriptEditSubmittedResponse: - """ - Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - segment_index : int - - original_text : str - The current segment text you are correcting (guards against applying to a changed segment). - - corrected_text : str - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - revision : typing.Optional[str] - The revision from the Premium transcript read. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptEditSubmittedResponse - Edit accepted, pending review - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.edits.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - segment_index=1, - original_text="originalText", - corrected_text="correctedText", - ) - """ - _response = self._raw_client.submit( - video_id, - segment_index=segment_index, - original_text=original_text, - corrected_text=corrected_text, - idempotency_key=idempotency_key, - revision=revision, - request_options=request_options, - ) - return _response.data - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.edits.withdraw( - video_id="video_id", - id="id", - ) - """ - _response = self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data - - -class AsyncEditsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawEditsClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawEditsClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawEditsClient - """ - return self._raw_client - - async def submit( - self, - video_id: str, - *, - segment_index: int, - original_text: str, - corrected_text: str, - idempotency_key: typing.Optional[str] = None, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> TranscriptEditSubmittedResponse: - """ - Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - segment_index : int - - original_text : str - The current segment text you are correcting (guards against applying to a changed segment). - - corrected_text : str - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - revision : typing.Optional[str] - The revision from the Premium transcript read. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - TranscriptEditSubmittedResponse - Edit accepted, pending review - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.edits.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - segment_index=1, - original_text="originalText", - corrected_text="correctedText", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.submit( - video_id, - segment_index=segment_index, - original_text=original_text, - corrected_text=corrected_text, - idempotency_key=idempotency_key, - revision=revision, - request_options=request_options, - ) - return _response.data - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.edits.withdraw( - video_id="video_id", - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data diff --git a/src/arcmira/transcripts/edits/raw_client.py b/src/arcmira/transcripts/edits/raw_client.py deleted file mode 100644 index c1b1411..0000000 --- a/src/arcmira/transcripts/edits/raw_client.py +++ /dev/null @@ -1,560 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.http_response import AsyncHttpResponse, HttpResponse -from ...core.jsonable_encoder import encode_path_param -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.conflict_error import ConflictError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.transcript_edit_submitted_response import TranscriptEditSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from pydantic import ValidationError - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawEditsClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def submit( - self, - video_id: str, - *, - segment_index: int, - original_text: str, - corrected_text: str, - idempotency_key: typing.Optional[str] = None, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[TranscriptEditSubmittedResponse]: - """ - Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - segment_index : int - - original_text : str - The current segment text you are correcting (guards against applying to a changed segment). - - corrected_text : str - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - revision : typing.Optional[str] - The revision from the Premium transcript read. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[TranscriptEditSubmittedResponse] - Edit accepted, pending review - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/edits", - method="POST", - json={ - "segmentIndex": segment_index, - "originalText": original_text, - "correctedText": corrected_text, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptEditSubmittedResponse, - parse_obj_as( - type_=TranscriptEditSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/edits/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawEditsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def submit( - self, - video_id: str, - *, - segment_index: int, - original_text: str, - corrected_text: str, - idempotency_key: typing.Optional[str] = None, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[TranscriptEditSubmittedResponse]: - """ - Purpose-built wrapper for the line_edit kind. The edit is pending review: visible to you immediately (returned in the transcript GET `edits[]`), applied for everyone once approved. Free (0 rows), attributed to your API key. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - segment_index : int - - original_text : str - The current segment text you are correcting (guards against applying to a changed segment). - - corrected_text : str - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - revision : typing.Optional[str] - The revision from the Premium transcript read. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptEditSubmittedResponse] - Edit accepted, pending review - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/edits", - method="POST", - json={ - "segmentIndex": segment_index, - "originalText": original_text, - "correctedText": corrected_text, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptEditSubmittedResponse, - parse_obj_as( - type_=TranscriptEditSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/edits/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/transcripts/merges/__init__.py b/src/arcmira/transcripts/merges/__init__.py deleted file mode 100644 index dadae70..0000000 --- a/src/arcmira/transcripts/merges/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file diff --git a/src/arcmira/transcripts/merges/client.py b/src/arcmira/transcripts/merges/client.py deleted file mode 100644 index ddc3e08..0000000 --- a/src/arcmira/transcripts/merges/client.py +++ /dev/null @@ -1,331 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.request_options import RequestOptions -from ...types.video_merge_list_response import VideoMergeListResponse -from ...types.video_merge_submitted_response import VideoMergeSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from .raw_client import AsyncRawMergesClient, RawMergesClient - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class MergesClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawMergesClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawMergesClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawMergesClient - """ - return self._raw_client - - def list(self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None) -> VideoMergeListResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoMergeListResponse - Success - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.merges.list( - video_id="video_id", - ) - """ - _response = self._raw_client.list(video_id, request_options=request_options) - return _response.data - - def submit( - self, - video_id: str, - *, - source_name: str, - target_entity_id: int, - idempotency_key: typing.Optional[str] = None, - replace_with: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> VideoMergeSubmittedResponse: - """ - Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - source_name : str - The name as it appears in this video (e.g. a first-name-only mention). - - target_entity_id : int - The canonical entity these mentions actually refer to. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - replace_with : typing.Optional[str] - Optional respelling applied to the transcript text (e.g. "Imad" → "Emad"). - - revision : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoMergeSubmittedResponse - Merge accepted, pending review - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.merges.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - source_name="sourceName", - target_entity_id=1, - ) - """ - _response = self._raw_client.submit( - video_id, - source_name=source_name, - target_entity_id=target_entity_id, - idempotency_key=idempotency_key, - replace_with=replace_with, - revision=revision, - request_options=request_options, - ) - return _response.data - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.merges.withdraw( - video_id="video_id", - id="id", - ) - """ - _response = self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data - - -class AsyncMergesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawMergesClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawMergesClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawMergesClient - """ - return self._raw_client - - async def list( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> VideoMergeListResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoMergeListResponse - Success - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.merges.list( - video_id="video_id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.list(video_id, request_options=request_options) - return _response.data - - async def submit( - self, - video_id: str, - *, - source_name: str, - target_entity_id: int, - idempotency_key: typing.Optional[str] = None, - replace_with: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> VideoMergeSubmittedResponse: - """ - Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - source_name : str - The name as it appears in this video (e.g. a first-name-only mention). - - target_entity_id : int - The canonical entity these mentions actually refer to. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - replace_with : typing.Optional[str] - Optional respelling applied to the transcript text (e.g. "Imad" → "Emad"). - - revision : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - VideoMergeSubmittedResponse - Merge accepted, pending review - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.merges.submit( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - source_name="sourceName", - target_entity_id=1, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.submit( - video_id, - source_name=source_name, - target_entity_id=target_entity_id, - idempotency_key=idempotency_key, - replace_with=replace_with, - revision=revision, - request_options=request_options, - ) - return _response.data - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.merges.withdraw( - video_id="video_id", - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data diff --git a/src/arcmira/transcripts/merges/raw_client.py b/src/arcmira/transcripts/merges/raw_client.py deleted file mode 100644 index b6d109e..0000000 --- a/src/arcmira/transcripts/merges/raw_client.py +++ /dev/null @@ -1,777 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.http_response import AsyncHttpResponse, HttpResponse -from ...core.jsonable_encoder import encode_path_param -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.conflict_error import ConflictError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.video_merge_list_response import VideoMergeListResponse -from ...types.video_merge_submitted_response import VideoMergeSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from pydantic import ValidationError - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawMergesClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def list( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[VideoMergeListResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[VideoMergeListResponse] - Success - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoMergeListResponse, - parse_obj_as( - type_=VideoMergeListResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def submit( - self, - video_id: str, - *, - source_name: str, - target_entity_id: int, - idempotency_key: typing.Optional[str] = None, - replace_with: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[VideoMergeSubmittedResponse]: - """ - Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - source_name : str - The name as it appears in this video (e.g. a first-name-only mention). - - target_entity_id : int - The canonical entity these mentions actually refer to. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - replace_with : typing.Optional[str] - Optional respelling applied to the transcript text (e.g. "Imad" → "Emad"). - - revision : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[VideoMergeSubmittedResponse] - Merge accepted, pending review - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges", - method="POST", - json={ - "sourceName": source_name, - "targetEntityId": target_entity_id, - "replaceWith": replace_with, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoMergeSubmittedResponse, - parse_obj_as( - type_=VideoMergeSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawMergesClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def list( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[VideoMergeListResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[VideoMergeListResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoMergeListResponse, - parse_obj_as( - type_=VideoMergeListResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def submit( - self, - video_id: str, - *, - source_name: str, - target_entity_id: int, - idempotency_key: typing.Optional[str] = None, - replace_with: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[VideoMergeSubmittedResponse]: - """ - Asserts that a name in this video refers to a specific entity, for misattributed name mentions in one video (e.g. a first-name-only mention resolved to the wrong entity). Optionally respells the transcript text via `replaceWith`. Pending review; applied optimistically for you. Mentions of the same name in other videos are untouched. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - source_name : str - The name as it appears in this video (e.g. a first-name-only mention). - - target_entity_id : int - The canonical entity these mentions actually refer to. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - replace_with : typing.Optional[str] - Optional respelling applied to the transcript text (e.g. "Imad" → "Emad"). - - revision : typing.Optional[str] - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[VideoMergeSubmittedResponse] - Merge accepted, pending review - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges", - method="POST", - json={ - "sourceName": source_name, - "targetEntityId": target_entity_id, - "replaceWith": replace_with, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoMergeSubmittedResponse, - parse_obj_as( - type_=VideoMergeSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/merges/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/transcripts/prepare.py b/src/arcmira/transcripts/prepare.py deleted file mode 100644 index 2d0770a..0000000 --- a/src/arcmira/transcripts/prepare.py +++ /dev/null @@ -1,179 +0,0 @@ -# Maintained by hand. scripts/install-generated.py copies this to -# src/arcmira/transcripts/prepare.py and makes the generated transcripts -# clients inherit these mixins, so regeneration keeps prepare_and_wait. - -from __future__ import annotations - -import asyncio -import time -import typing -import uuid - -from ..types.transcript_job import TranscriptJob -from ..types.transcript_result import TranscriptResult_Ready - -if typing.TYPE_CHECKING: - from .client import AsyncTranscriptsClient, TranscriptsClient - -DEFAULT_POLL_SECONDS = 5 - - -class PreparationError(Exception): - """Base class for prepare_and_wait refusals that are not HTTP errors.""" - - -class PreparationTimeoutError(PreparationError): - """The Premium job was still pending when timeout_seconds ran out. - - The job keeps running. Read the transcript later, or poll `job.status_url`. - """ - - def __init__(self, job: TranscriptJob) -> None: - super().__init__(f"Premium job {job.id} for {job.video_id} is still {job.status}") - self.job = job - - -class PreparationFailedError(PreparationError): - """The Premium job ended failed or refunded, or finished without a servable transcript.""" - - def __init__(self, job: TranscriptJob) -> None: - super().__init__(f"Premium job {job.id} for {job.video_id} ended {job.state}: {job.error or job.status}") - self.job = job - - -class PremiumUnavailableError(PreparationError): - """The account was served captions instead of Premium, so nothing can be prepared.""" - - def __init__(self, transcript: TranscriptResult_Ready) -> None: - super().__init__(f"Premium is not available for this account; the read returned {transcript.quality}") - self.transcript = transcript - - -# One step of the plan. The sync and async clients run the same plan, so -# they cannot drift on when to submit, when to poll, or when to give up. -_READ, _SUBMIT, _STATUS, _SLEEP = "read", "submit", "status", "sleep" -_Step = typing.Tuple[str, typing.Any] - - -def _delay(response: typing.Any, job: TranscriptJob) -> float: - header = response.response.headers.get("retry-after") - if header is not None and header.strip().isdigit(): - return float(header) - return float(job.next_poll_seconds if job.next_poll_seconds is not None else DEFAULT_POLL_SECONDS) - - -def _owned(transcript: typing.Any) -> TranscriptResult_Ready: - if transcript.quality != "premium": - raise PremiumUnavailableError(transcript) - return transcript - - -def _plan( - video_id: str, max_on_demand_cents: int, timeout_seconds: float -) -> typing.Generator[_Step, typing.Any, TranscriptResult_Ready]: - if max_on_demand_cents < 0: - raise ValueError("max_on_demand_cents cannot be negative") - deadline = time.monotonic() + timeout_seconds - read = yield (_READ, None) - transcript = read.data - if transcript.state == "ready": - return _owned(transcript) - if transcript.state == "pending": - response, job = read, transcript.job - else: - submission: typing.Dict[str, typing.Any] = {"video_id": video_id} - if max_on_demand_cents > 0: - # Spending money is the only case that needs a key; one key serves this call and its own retries. - submission.update(max_on_demand_cents=max_on_demand_cents, idempotency_key=str(uuid.uuid4())) - if transcript.quote is not None: - submission["max_rows"] = transcript.quote.rows - response = yield (_SUBMIT, submission) - job = response.data.job - while job.state == "pending": - remaining = deadline - time.monotonic() - if remaining <= 0: - raise PreparationTimeoutError(job) - yield (_SLEEP, min(_delay(response, job), remaining)) - response = yield (_STATUS, job.id) - job = response.data - if job.state != "ready": - raise PreparationFailedError(job) - final = (yield (_READ, None)).data - if final.state != "ready": - raise PreparationFailedError(job) - return _owned(final) - - -class PrepareAndWait: - def prepare_and_wait( - self: TranscriptsClient, - video_id: str, - *, - max_on_demand_cents: int = 0, - timeout_seconds: float = 300, - ) -> TranscriptResult_Ready: - """ - Read the Premium transcript for a video, preparing it first when the account does not own it. - - Reads `quality="premium"`. A ready transcript is returned as is. When preparation is required, - this posts `{video_id}` once, polls the job at the pace the API asks for, and returns the - transcript when it is ready. The default spends included credits only and moves no money. - A positive `max_on_demand_cents` authorizes that many cents of on-demand spend, and the call - sends an Idempotency-Key and the quoted `max_rows`. - - Raises `PreparationTimeoutError` (carrying the job) when `timeout_seconds` pass first, - `PreparationFailedError` when the job fails or is refunded, and `PremiumUnavailableError` - when the plan has no Premium. API refusals raise `ApiError`. - """ - plan = _plan(video_id, max_on_demand_cents, timeout_seconds) - raw = self.with_raw_response - try: - step = next(plan) - while True: - kind, argument = step - if kind == _SLEEP: - time.sleep(argument) - reply = None - elif kind == _READ: - reply = raw.get(video_id, quality="premium") - elif kind == _SUBMIT: - reply = raw.request(**argument) - else: - reply = raw.status(argument) - step = plan.send(reply) - except StopIteration as done: - return done.value - - -class AsyncPrepareAndWait: - async def prepare_and_wait( - self: AsyncTranscriptsClient, - video_id: str, - *, - max_on_demand_cents: int = 0, - timeout_seconds: float = 300, - ) -> TranscriptResult_Ready: - """ - Read the Premium transcript for a video, preparing it first when the account does not own it. - - The asynchronous twin of `Arcmira.transcripts.prepare_and_wait`; it takes the same arguments, - follows the same plan and raises the same errors. - """ - plan = _plan(video_id, max_on_demand_cents, timeout_seconds) - raw = self.with_raw_response - try: - step = next(plan) - while True: - kind, argument = step - if kind == _SLEEP: - await asyncio.sleep(argument) - reply = None - elif kind == _READ: - reply = await raw.get(video_id, quality="premium") - elif kind == _SUBMIT: - reply = await raw.request(**argument) - else: - reply = await raw.status(argument) - step = plan.send(reply) - except StopIteration as done: - return done.value diff --git a/src/arcmira/transcripts/raw_client.py b/src/arcmira/transcripts/raw_client.py index f6cd246..824d251 100644 --- a/src/arcmira/transcripts/raw_client.py +++ b/src/arcmira/transcripts/raw_client.py @@ -12,7 +12,6 @@ from ..core.pydantic_utilities import parse_obj_as from ..core.request_options import RequestOptions from ..errors.bad_request_error import BadRequestError -from ..errors.conflict_error import ConflictError from ..errors.forbidden_error import ForbiddenError from ..errors.internal_server_error import InternalServerError from ..errors.not_found_error import NotFoundError @@ -21,21 +20,15 @@ from ..errors.too_many_requests_error import TooManyRequestsError from ..errors.unauthorized_error import UnauthorizedError from ..types.error import Error -from ..types.transcript_job import TranscriptJob from ..types.transcript_purchase_quote import TranscriptPurchaseQuote from ..types.transcript_request_list_response import TranscriptRequestListResponse from ..types.transcript_request_list_response_requests_item import TranscriptRequestListResponseRequestsItem -from ..types.transcript_request_submit_response import TranscriptRequestSubmitResponse from ..types.transcript_result import TranscriptResult from ..types.transcript_search_response import TranscriptSearchResponse -from ..types.video_captions_response import VideoCaptionsResponse from .types.get_transcripts_request_quality import GetTranscriptsRequestQuality from .types.search_transcripts_request_source import SearchTranscriptsRequestSource from pydantic import ValidationError -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - class RawTranscriptsClient: def __init__(self, *, client_wrapper: SyncClientWrapper): @@ -51,14 +44,14 @@ def search( about: typing.Optional[str] = None, by: typing.Optional[str] = None, kind: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, source: typing.Optional[SearchTranscriptsRequestSource] = None, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[TranscriptSearchResponse]: """ - Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- @@ -81,13 +74,13 @@ def search( Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. kind : typing.Optional[str] - Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk. + Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk. - published_after : typing.Optional[str] - ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. + after : typing.Optional[str] + Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. - published_before : typing.Optional[str] - ISO date. Only media published before this day. + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. source : typing.Optional[SearchTranscriptsRequestSource] Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. @@ -104,7 +97,7 @@ def search( Success """ _response = self._client_wrapper.httpx_client.request( - "v1/transcripts/search", + "v1/search", method="GET", params={ "q": q, @@ -114,8 +107,8 @@ def search( "about": about, "by": by, "kind": kind, - "published_after": published_after, - "published_before": published_before, + "after": after, + "before": before, "source": source, "limit": limit, }, @@ -241,7 +234,7 @@ def get( request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[TranscriptResult]: """ - Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. Parameters ---------- @@ -249,7 +242,7 @@ def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -272,7 +265,7 @@ def get( Returns ------- HttpResponse[TranscriptResult] - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state preparation_required: Premium is not owned yet; the quote and the POST that prepares it. + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). """ _response = self._client_wrapper.httpx_client.request( f"v1/transcripts/{encode_path_param(video_id)}", @@ -398,7 +391,7 @@ def quote( self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[TranscriptPurchaseQuote]: """ - Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. + Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. Parameters ---------- @@ -503,40 +496,65 @@ def quote( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def captions( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[VideoCaptionsResponse]: + def list_requests( + self, + *, + video_id: typing.Optional[str] = None, + limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, + request_options: typing.Optional[RequestOptions] = None, + ) -> SyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse]: """ - Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background. + Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`. Parameters ---------- - video_id : str - YouTube video id, 11 characters. + video_id : typing.Optional[str] + Filter to your requests for one video. + + limit : typing.Optional[int] + Requests per page, from 1 to 100. Default 20. + + cursor : typing.Optional[str] + Signed continuation from next_cursor. Keep the same filter, limit and credential. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[VideoCaptionsResponse] + SyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse] Success """ _response = self._client_wrapper.httpx_client.request( - f"v1/videos/{encode_path_param(video_id)}/captions", + "v1/transcriptions", method="GET", + params={ + "video_id": video_id, + "limit": limit, + "cursor": cursor, + }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoCaptionsResponse, + _parsed_response = typing.cast( + TranscriptRequestListResponse, parse_obj_as( - type_=VideoCaptionsResponse, # type: ignore + type_=TranscriptRequestListResponse, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + _items = _parsed_response.requests + _parsed_next = _parsed_response.next_cursor + _has_next = _parsed_next is not None and _parsed_next != "" + _get_next = lambda: self.list_requests( + video_id=video_id, + limit=limit, + cursor=_parsed_next, + request_options=request_options, + ) + return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -603,17 +621,6 @@ def captions( ), ), ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) @@ -623,65 +630,101 @@ def captions( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def list_requests( + +class AsyncRawTranscriptsClient: + def __init__(self, *, client_wrapper: AsyncClientWrapper): + self._client_wrapper = client_wrapper + + async def search( self, *, - video_id: typing.Optional[str] = None, + q: str, + channel_ids: typing.Optional[str] = None, + channel: typing.Optional[str] = None, + entity_ids: typing.Optional[str] = None, + about: typing.Optional[str] = None, + by: typing.Optional[str] = None, + kind: typing.Optional[str] = None, + after: typing.Optional[str] = None, + before: typing.Optional[str] = None, + source: typing.Optional[SearchTranscriptsRequestSource] = None, limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> SyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse]: + ) -> AsyncHttpResponse[TranscriptSearchResponse]: """ - Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`. + Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to sponsored, organic or mention passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. An after later than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. Parameters ---------- - video_id : typing.Optional[str] - Filter to your requests for one video. + q : str + One topic or phrase. Do not concatenate unrelated names; make one call per topic. - limit : typing.Optional[int] - Requests per page, from 1 to 100. Default 20. + channel_ids : typing.Optional[str] + Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - cursor : typing.Optional[str] - Signed continuation from next_cursor. Keep the same filter, limit and credential. + channel : typing.Optional[str] + Alias of channel_ids for code-mode clients; the union of both is the scope. + + entity_ids : typing.Optional[str] + Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + + about : typing.Optional[str] + Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + + by : typing.Optional[str] + Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. + + kind : typing.Optional[str] + Comma-separated passage classes: sponsored, organic, mention. Combine with about to read what was said about a brand in ad reads or in organic talk. + + after : typing.Optional[str] + Only media published at or after this instant. An after later than your plan's freshness gate is refused with freshness_requires_paid rather than widened. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. + + before : typing.Optional[str] + Only media published before this instant, so before=2026-09-02 includes all of 2026-09-01. An ISO 8601 date (2026-09-01) or datetime with offset (2026-09-01T00:00:00Z), read in UTC. The window is half-open: after is inclusive, before is exclusive. + + source : typing.Optional[SearchTranscriptsRequestSource] + Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. + + limit : typing.Optional[int] + Chunks to return, 1 to 20. Default 5. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - SyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse] + AsyncHttpResponse[TranscriptSearchResponse] Success """ - _response = self._client_wrapper.httpx_client.request( - "v1/transcriptions", + _response = await self._client_wrapper.httpx_client.request( + "v1/search", method="GET", params={ - "video_id": video_id, + "q": q, + "channel_ids": channel_ids, + "channel": channel, + "entity_ids": entity_ids, + "about": about, + "by": by, + "kind": kind, + "after": after, + "before": before, + "source": source, "limit": limit, - "cursor": cursor, }, request_options=request_options, ) try: if 200 <= _response.status_code < 300: - _parsed_response = typing.cast( - TranscriptRequestListResponse, + _data = typing.cast( + TranscriptSearchResponse, parse_obj_as( - type_=TranscriptRequestListResponse, # type: ignore + type_=TranscriptSearchResponse, # type: ignore object_=_response.json(), ), ) - _items = _parsed_response.requests - _parsed_next = _parsed_response.next_cursor - _has_next = _parsed_next is not None and _parsed_next != "" - _get_next = lambda: self.list_requests( - video_id=video_id, - limit=limit, - cursor=_parsed_next, - request_options=request_options, - ) - return SyncPager(has_next=_has_next, items=_items, get_next=_get_next, response=_parsed_response) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -704,6 +747,17 @@ def list_requests( ), ), ) + if _response.status_code == 402: + raise PaymentRequiredError( + headers=dict(_response.headers), + body=typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) if _response.status_code == 403: raise ForbiddenError( headers=dict(_response.headers), @@ -748,6 +802,17 @@ def list_requests( ), ), ) + if _response.status_code == 503: + raise ServiceUnavailableError( + headers=dict(_response.headers), + body=typing.cast( + Error, + parse_obj_as( + type_=Error, # type: ignore + object_=_response.json(), + ), + ), + ) _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) @@ -757,70 +822,75 @@ def list_requests( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def request( + async def get( self, + video_id: str, *, - idempotency_key: typing.Optional[str] = None, - video_id: typing.Optional[str] = OMIT, - url: typing.Optional[str] = OMIT, - max_rows: typing.Optional[int] = OMIT, - max_on_demand_cents: typing.Optional[int] = OMIT, + quality: typing.Optional[GetTranscriptsRequestQuality] = None, + language: typing.Optional[str] = None, + timestamps: typing.Optional[bool] = None, + start: typing.Optional[float] = None, + end: typing.Optional[float] = None, + refresh: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[TranscriptRequestSubmitResponse]: + ) -> AsyncHttpResponse[TranscriptResult]: """ - Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. Parameters ---------- - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. + video_id : str + YouTube video id, 11 characters. - video_id : typing.Optional[str] - YouTube video id (11 characters). Either video_id or url is required. + quality : typing.Optional[GetTranscriptsRequestQuality] + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. + + language : typing.Optional[str] + Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. + + timestamps : typing.Optional[bool] + false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true. - url : typing.Optional[str] - A YouTube watch/short/live URL. Either video_id or url is required. + start : typing.Optional[float] + Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge. - max_rows : typing.Optional[int] - Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0. + end : typing.Optional[float] + Window end in seconds, greater than start and no greater than the video duration. Send start and end together. - max_on_demand_cents : typing.Optional[int] - Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows. + refresh : typing.Optional[bool] + Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[TranscriptRequestSubmitResponse] - job.state is ready, failed or refunded: the transcript is servable, or the purchase ended without one. + AsyncHttpResponse[TranscriptResult] + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). """ - _response = self._client_wrapper.httpx_client.request( - "v1/transcriptions", - method="POST", - json={ - "video_id": video_id, - "url": url, - "max_rows": max_rows, - "max_on_demand_cents": max_on_demand_cents, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, + _response = await self._client_wrapper.httpx_client.request( + f"v1/transcripts/{encode_path_param(video_id)}", + method="GET", + params={ + "quality": quality, + "language": language, + "timestamps": timestamps, + "start": start, + "end": end, + "refresh": refresh, }, request_options=request_options, - omit=OMIT, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - TranscriptRequestSubmitResponse, + TranscriptResult, parse_obj_as( - type_=TranscriptRequestSubmitResponse, # type: ignore + type_=TranscriptResult, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -876,8 +946,8 @@ def request( ), ), ) - if _response.status_code == 409: - raise ConflictError( + if _response.status_code == 429: + raise TooManyRequestsError( headers=dict(_response.headers), body=typing.cast( Error, @@ -887,8 +957,8 @@ def request( ), ), ) - if _response.status_code == 429: - raise TooManyRequestsError( + if _response.status_code == 500: + raise InternalServerError( headers=dict(_response.headers), body=typing.cast( Error, @@ -898,8 +968,8 @@ def request( ), ), ) - if _response.status_code == 500: - raise InternalServerError( + if _response.status_code == 503: + raise ServiceUnavailableError( headers=dict(_response.headers), body=typing.cast( Error, @@ -918,40 +988,40 @@ def request( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - def status( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[TranscriptJob]: + async def quote( + self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None + ) -> AsyncHttpResponse[TranscriptPurchaseQuote]: """ - Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up. + Optional free quote: the price a Premium read of this video would charge right now, as rows and credits, where the credits would come from, and max_on_demand_cents, the on-demand money the read would need beyond included credits within the account limit. It does not reserve funds or start generation. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. Parameters ---------- - id : str - Transcription request id, the UUID POST /v1/transcriptions returned. + video_id : str + YouTube video id, 11 characters. request_options : typing.Optional[RequestOptions] Request-specific configuration. Returns ------- - HttpResponse[TranscriptJob] + AsyncHttpResponse[TranscriptPurchaseQuote] Success """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcriptions/{encode_path_param(id)}", + _response = await self._client_wrapper.httpx_client.request( + f"v1/transcripts/{encode_path_param(video_id)}/quote", method="GET", request_options=request_options, ) try: if 200 <= _response.status_code < 300: _data = typing.cast( - TranscriptJob, + TranscriptPurchaseQuote, parse_obj_as( - type_=TranscriptJob, # type: ignore + type_=TranscriptPurchaseQuote, # type: ignore object_=_response.json(), ), ) - return HttpResponse(response=_response, data=_data) + return AsyncHttpResponse(response=_response, data=_data) if _response.status_code == 400: raise BadRequestError( headers=dict(_response.headers), @@ -1027,611 +1097,24 @@ def status( ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - -class AsyncRawTranscriptsClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def search( + async def list_requests( self, *, - q: str, - channel_ids: typing.Optional[str] = None, - channel: typing.Optional[str] = None, - entity_ids: typing.Optional[str] = None, - about: typing.Optional[str] = None, - by: typing.Optional[str] = None, - kind: typing.Optional[str] = None, - published_after: typing.Optional[str] = None, - published_before: typing.Optional[str] = None, - source: typing.Optional[SearchTranscriptsRequestSource] = None, + video_id: typing.Optional[str] = None, limit: typing.Optional[int] = None, + cursor: typing.Optional[str] = None, request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[TranscriptSearchResponse]: + ) -> AsyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse]: """ - Search indexed YouTube and podcast transcripts for short spoken slices. Each result includes spoken text, a watch URL, and a publish date. Scope with channel_ids (or channel) and entity_ids (a person id filters to that person's appearances); narrow to passages about entities with about, to a speaker with by, and to mention, recommendation_sponsored or recommendation_organic passages with kind. Every filter takes ids, never names: resolve a name first with GET /v1/entities/resolve, or the call answers 400 id_required naming the parameter. Results carry names beside ids (filters.about, filters.by, chunk about and speakers_by). Use one topic per call. Search results include text on every plan within the plan's publication-date window. Explicitly requesting source=arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. A published_after narrower than the plan's freshness gate is refused with freshness_requires_paid rather than widened. Bills one row per chunk returned. Every gate is a typed error whose error.unlock.url names the plan that lifts it; pass src=mcp-tool only from the Arcmira MCP server. + Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`. Parameters ---------- - q : str - One topic or phrase. Do not concatenate unrelated names; make one call per topic. + video_id : typing.Optional[str] + Filter to your requests for one video. - channel_ids : typing.Optional[str] - Comma-separated YouTube channel ids (UC...), at most 8. Pass every show in scope unless drilling into one. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - - channel : typing.Optional[str] - Alias of channel_ids for code-mode clients; the union of both is the scope. - - entity_ids : typing.Optional[str] - Comma-separated entity ids (ent_{n}), at most 8. A person id filters to that person's appearances; a channel id widens channel_ids. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - - about : typing.Optional[str] - Comma-separated entity ids (ent_{n}), at most 8. Only passages about these entities: excerpt pins, exact-name mentions and ad verdicts. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - - by : typing.Optional[str] - Comma-separated person ids (ent_{n}), at most 8. Only passages where one of these people says the query words (each line of a chunk is labeled with its speaker); a non-person id is refused with invalid_query naming its type. Speaker labels cover a minority of shows; an empty result carries a note saying whether the person is labeled anywhere. Ids only: a name answers 400 id_required. Resolve names first with GET /v1/entities/resolve. - - kind : typing.Optional[str] - Comma-separated passage kinds: mention, recommendation_sponsored, recommendation_organic. Combine with about to read what was said about a brand in ad reads or in organic talk. - - published_after : typing.Optional[str] - ISO date. Only media published on or after this day. A window narrower than your plan's freshness gate is refused with freshness_requires_paid rather than widened. - - published_before : typing.Optional[str] - ISO date. Only media published before this day. - - source : typing.Optional[SearchTranscriptsRequestSource] - Restrict to one transcript source class. arcmira_premium on a plan without Premium transcripts is refused with filter_requires_paid. - - limit : typing.Optional[int] - Chunks to return, 1 to 20. Default 5. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptSearchResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/transcripts/search", - method="GET", - params={ - "q": q, - "channel_ids": channel_ids, - "channel": channel, - "entity_ids": entity_ids, - "about": about, - "by": by, - "kind": kind, - "published_after": published_after, - "published_before": published_before, - "source": source, - "limit": limit, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptSearchResponse, - parse_obj_as( - type_=TranscriptSearchResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def get( - self, - video_id: str, - *, - quality: typing.Optional[GetTranscriptsRequestQuality] = None, - language: typing.Optional[str] = None, - timestamps: typing.Optional[bool] = None, - start: typing.Optional[float] = None, - end: typing.Optional[float] = None, - refresh: typing.Optional[bool] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[TranscriptResult]: - """ - Caption retrieval costs one row per started 15 minutes. Premium retrieval is free and never buys, generates, or returns fallback captions. Branch on state: 200 ready is owned content; 202 pending carries the job, with Retry-After; 200 preparation_required carries the whole-video quote and the action to take, POST /v1/transcriptions with { video_id }, plus last_attempt when the previous purchase failed. Plans without Premium read captions with an access gate instead. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is read-only: an owned transcript returns 200 state ready at zero rows, an active purchase returns 202 state pending with its job, and an unowned transcript on a plan with Premium returns 200 state preparation_required with the quote and the POST /v1/transcriptions action. It never purchases or substitutes captions. Default captions unless changed in account settings. - - language : typing.Optional[str] - Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. - - timestamps : typing.Optional[bool] - false returns paragraphs[] of { start, text, speaker? } instead of lines[], for reading rather than citing. Default true. - - start : typing.Optional[float] - Window start in seconds from the beginning of the video. Send start and end together. On captions the window bills only its own started 15-minute blocks; on Premium it trims an already purchased transcript; this GET does not charge. - - end : typing.Optional[float] - Window end in seconds, greater than start and no greater than the video duration. Send start and end together. - - refresh : typing.Optional[bool] - Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptResult] - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state preparation_required: Premium is not owned yet; the quote and the POST that prepares it. - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}", - method="GET", - params={ - "quality": quality, - "language": language, - "timestamps": timestamps, - "start": start, - "end": end, - "refresh": refresh, - }, - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptResult, - parse_obj_as( - type_=TranscriptResult, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def quote( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[TranscriptPurchaseQuote]: - """ - Optional free quote. It does not reserve funds or start generation. max_rows authorizes rows, while max_on_demand_cents separately authorizes new money and defaults to zero on purchase. The accepted purchase stores its pricing mode. A video with no known duration, or one past the 12 hour cap, answers 400 invalid_query with param video_id. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptPurchaseQuote] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/quote", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptPurchaseQuote, - parse_obj_as( - type_=TranscriptPurchaseQuote, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def captions( - self, video_id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[VideoCaptionsResponse]: - """ - Free (0 rows), any key. Returns the video metadata and every caption track YouTube lists for it, each as { code, name, generated }. Call it when GET /v1/transcripts/{video_id} answered transcript_unavailable without languages, or before asking for a specific track. Listing is served from a day-long cache; a cold listing answers 503 transcript_fetching with Retry-After while the fetch continues in the background. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[VideoCaptionsResponse] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/videos/{encode_path_param(video_id)}/captions", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - VideoCaptionsResponse, - parse_obj_as( - type_=VideoCaptionsResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 503: - raise ServiceUnavailableError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def list_requests( - self, - *, - video_id: typing.Optional[str] = None, - limit: typing.Optional[int] = None, - cursor: typing.Optional[str] = None, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncPager[TranscriptRequestListResponseRequestsItem, TranscriptRequestListResponse]: - """ - Your transcription requests in descending creation time and id order. limit defaults to 20 and accepts 1–100. Follow next_cursor with the same video_id, limit and credential; has_more is false and next_cursor is null on the last page. A traversal excludes requests inserted after its first page. Each entry has the same shape as the status poll plus a `title` field (the video title, null when unknown). The scheduled reconciler advances requests; reading this list never dispatches work or changes billing. In-flight entries carry `eta_seconds` and `next_poll_seconds`. - - Parameters - ---------- - video_id : typing.Optional[str] - Filter to your requests for one video. - - limit : typing.Optional[int] - Requests per page, from 1 to 100. Default 20. + limit : typing.Optional[int] + Requests per page, from 1 to 100. Default 20. cursor : typing.Optional[str] Signed continuation from next_cursor. Keep the same filter, limit and credential. @@ -1750,273 +1233,3 @@ async def _get_next(): status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e ) raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def request( - self, - *, - idempotency_key: typing.Optional[str] = None, - video_id: typing.Optional[str] = OMIT, - url: typing.Optional[str] = OMIT, - max_rows: typing.Optional[int] = OMIT, - max_on_demand_cents: typing.Optional[int] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[TranscriptRequestSubmitResponse]: - """ - Explicit whole-video Premium purchase in one request: POST { video_id }. With no max_rows the purchase is capped at the current quote, and max_on_demand_cents defaults to zero, so it spends included rows or credits only and moves no money. Idempotency-Key is optional: without one, a purchase already open or owned for this video is returned with existing: true, and two simultaneous requests buy once. max_on_demand_cents above 0 is the only way to move money and requires both Idempotency-Key and max_rows (400 invalid_body names the missing one in param). A video with no known duration or longer than 12 hours answers 400 invalid_body with param video_id. A plan without Premium answers 403 paid_plan_required with unlock. Accepted price, mode, and debit identity persist across retries. Included rows or credits are reserved up front; monetary on-demand usage is reserved until Premium is ready. Existing owned unlocks cost zero. A terminal generation failure refunds the exact original debit and period before reporting refunded. A repeated key returns the same request; different intent with that key returns idempotency_conflict. The body is { job, existing }. Poll job.status_url after Retry-After. A pending job returns 202; a ready, failed or refunded job returns 200. Idempotency-Replayed: true marks a replay of the same key; a different key joined onto the active purchase answers existing: true without it. - - Parameters - ---------- - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - video_id : typing.Optional[str] - YouTube video id (11 characters). Either video_id or url is required. - - url : typing.Optional[str] - A YouTube watch/short/live URL. Either video_id or url is required. - - max_rows : typing.Optional[int] - Maximum whole-video rows authorized. Credit mode charges four credits per row. Omit it to cap the purchase at the current quote. Required with max_on_demand_cents above 0. - - max_on_demand_cents : typing.Optional[int] - Maximum new monetary on-demand charge in whole cents. Defaults to 0, which moves no money. Above 0 it requires Idempotency-Key and max_rows. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptRequestSubmitResponse] - job.state is ready, failed or refunded: the transcript is servable, or the purchase ended without one. - """ - _response = await self._client_wrapper.httpx_client.request( - "v1/transcriptions", - method="POST", - json={ - "video_id": video_id, - "url": url, - "max_rows": max_rows, - "max_on_demand_cents": max_on_demand_cents, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptRequestSubmitResponse, - parse_obj_as( - type_=TranscriptRequestSubmitResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 402: - raise PaymentRequiredError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def status( - self, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[TranscriptJob]: - """ - Agent-friendly polling contract: while the request is in flight the response carries a Retry-After header (seconds) and body fields `eta_seconds` and `next_poll_seconds`. Sleep on Retry-After and re-poll. `status` walks queued → downloading → transcribing → analyzing → complete (user-facing `stage` folds downloading into transcribing). refund_pending retains Retry-After and next_poll_seconds until reversal completes; it has no completion ETA. Terminal statuses (`complete`, `failed`, `refunded`) drop Retry-After. On `complete`, fetch the transcript via GET /v1/transcripts/{video_id}; the successful purchase owns the permanent unlock. `refunded` means the pipeline failed and the rows were returned. A caller with no account holds no jobs: it is refused with 401 job_requires_account, whose unlock points at sign-up. - - Parameters - ---------- - id : str - Transcription request id, the UUID POST /v1/transcriptions returned. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[TranscriptJob] - Success - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcriptions/{encode_path_param(id)}", - method="GET", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - TranscriptJob, - parse_obj_as( - type_=TranscriptJob, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/transcripts/speakers/__init__.py b/src/arcmira/transcripts/speakers/__init__.py deleted file mode 100644 index dadae70..0000000 --- a/src/arcmira/transcripts/speakers/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -# isort: skip_file diff --git a/src/arcmira/transcripts/speakers/client.py b/src/arcmira/transcripts/speakers/client.py deleted file mode 100644 index bcc2e9f..0000000 --- a/src/arcmira/transcripts/speakers/client.py +++ /dev/null @@ -1,266 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.request_options import RequestOptions -from ...types.speaker_identification_submitted_response import SpeakerIdentificationSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from .raw_client import AsyncRawSpeakersClient, RawSpeakersClient - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class SpeakersClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._raw_client = RawSpeakersClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> RawSpeakersClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - RawSpeakersClient - """ - return self._raw_client - - def identify( - self, - video_id: str, - *, - speaker_id: int, - idempotency_key: typing.Optional[str] = None, - entity_id: typing.Optional[int] = OMIT, - name: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SpeakerIdentificationSubmittedResponse: - """ - Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - speaker_id : int - A speakers[].id from the Premium transcript read that revision names. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - entity_id : typing.Optional[int] - Existing person entity id. Either entityId or name is required. - - name : typing.Optional[str] - Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required. - - revision : typing.Optional[str] - The revision of the transcript read speakerId came from. Required. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SpeakerIdentificationSubmittedResponse - Identification accepted, pending review - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.speakers.identify( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - speaker_id=1, - ) - """ - _response = self._raw_client.identify( - video_id, - speaker_id=speaker_id, - idempotency_key=idempotency_key, - entity_id=entity_id, - name=name, - revision=revision, - request_options=request_options, - ) - return _response.data - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Withdrawing also removes the community-attributed appearance the identification created. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - from arcmira import Arcmira - - client = Arcmira( - api_key="YOUR_API_KEY", - ) - client.transcripts.speakers.withdraw( - video_id="video_id", - id="id", - ) - """ - _response = self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data - - -class AsyncSpeakersClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._raw_client = AsyncRawSpeakersClient(client_wrapper=client_wrapper) - - @property - def with_raw_response(self) -> AsyncRawSpeakersClient: - """ - Retrieves a raw implementation of this client that returns raw responses. - - Returns - ------- - AsyncRawSpeakersClient - """ - return self._raw_client - - async def identify( - self, - video_id: str, - *, - speaker_id: int, - idempotency_key: typing.Optional[str] = None, - entity_id: typing.Optional[int] = OMIT, - name: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> SpeakerIdentificationSubmittedResponse: - """ - Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - speaker_id : int - A speakers[].id from the Premium transcript read that revision names. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - entity_id : typing.Optional[int] - Existing person entity id. Either entityId or name is required. - - name : typing.Optional[str] - Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required. - - revision : typing.Optional[str] - The revision of the transcript read speakerId came from. Required. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - SpeakerIdentificationSubmittedResponse - Identification accepted, pending review - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.speakers.identify( - video_id="video_id", - idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - speaker_id=1, - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.identify( - video_id, - speaker_id=speaker_id, - idempotency_key=idempotency_key, - entity_id=entity_id, - name=name, - revision=revision, - request_options=request_options, - ) - return _response.data - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> WithdrawnResponse: - """ - Withdrawing also removes the community-attributed appearance the identification created. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - WithdrawnResponse - Withdrawn - - Examples - -------- - import asyncio - - from arcmira import AsyncArcmira - - client = AsyncArcmira( - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.transcripts.speakers.withdraw( - video_id="video_id", - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._raw_client.withdraw(video_id, id, request_options=request_options) - return _response.data diff --git a/src/arcmira/transcripts/speakers/raw_client.py b/src/arcmira/transcripts/speakers/raw_client.py deleted file mode 100644 index fac6f4f..0000000 --- a/src/arcmira/transcripts/speakers/raw_client.py +++ /dev/null @@ -1,568 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing -from json.decoder import JSONDecodeError - -from ...core.api_error import ApiError -from ...core.client_wrapper import AsyncClientWrapper, SyncClientWrapper -from ...core.http_response import AsyncHttpResponse, HttpResponse -from ...core.jsonable_encoder import encode_path_param -from ...core.parse_error import ParsingError -from ...core.pydantic_utilities import parse_obj_as -from ...core.request_options import RequestOptions -from ...errors.bad_request_error import BadRequestError -from ...errors.conflict_error import ConflictError -from ...errors.forbidden_error import ForbiddenError -from ...errors.internal_server_error import InternalServerError -from ...errors.not_found_error import NotFoundError -from ...errors.too_many_requests_error import TooManyRequestsError -from ...errors.unauthorized_error import UnauthorizedError -from ...types.error import Error -from ...types.speaker_identification_submitted_response import SpeakerIdentificationSubmittedResponse -from ...types.withdrawn_response import WithdrawnResponse -from pydantic import ValidationError - -# this is used as the default value for optional parameters -OMIT = typing.cast(typing.Any, ...) - - -class RawSpeakersClient: - def __init__(self, *, client_wrapper: SyncClientWrapper): - self._client_wrapper = client_wrapper - - def identify( - self, - video_id: str, - *, - speaker_id: int, - idempotency_key: typing.Optional[str] = None, - entity_id: typing.Optional[int] = OMIT, - name: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> HttpResponse[SpeakerIdentificationSubmittedResponse]: - """ - Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - speaker_id : int - A speakers[].id from the Premium transcript read that revision names. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - entity_id : typing.Optional[int] - Existing person entity id. Either entityId or name is required. - - name : typing.Optional[str] - Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required. - - revision : typing.Optional[str] - The revision of the transcript read speakerId came from. Required. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[SpeakerIdentificationSubmittedResponse] - Identification accepted, pending review - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/speakers", - method="POST", - json={ - "speakerId": speaker_id, - "entityId": entity_id, - "name": name, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SpeakerIdentificationSubmittedResponse, - parse_obj_as( - type_=SpeakerIdentificationSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> HttpResponse[WithdrawnResponse]: - """ - Withdrawing also removes the community-attributed appearance the identification created. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - HttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/speakers/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return HttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - -class AsyncRawSpeakersClient: - def __init__(self, *, client_wrapper: AsyncClientWrapper): - self._client_wrapper = client_wrapper - - async def identify( - self, - video_id: str, - *, - speaker_id: int, - idempotency_key: typing.Optional[str] = None, - entity_id: typing.Optional[int] = OMIT, - name: typing.Optional[str] = OMIT, - revision: typing.Optional[str] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> AsyncHttpResponse[SpeakerIdentificationSubmittedResponse]: - """ - Links a diarization speaker id to a person entity (or proposes a new person via `name`). Creates a community-attributed appearance immediately. It shows on the person page right away, flagged pending review; reviewers can revert it. Free (0 rows). - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - speaker_id : int - A speakers[].id from the Premium transcript read that revision names. - - idempotency_key : typing.Optional[str] - 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Persist a unique key and the exact request before sending a logical mutation. A retry returns its stored response with Idempotency-Replayed: true. A changed intent under a finalized key returns 409 idempotency_conflict. Keys belong to the authenticated owner, credential and mutation domain. Current authorization still applies. Receipts have no general 24-hour expiry; signing-secret recovery alone expires after 24 hours or when the secret is displaced. - - entity_id : typing.Optional[int] - Existing person entity id. Either entityId or name is required. - - name : typing.Optional[str] - Propose a person not in the index yet. Reuses an existing same-name person or creates a provisional one. Either entityId or name is required. - - revision : typing.Optional[str] - The revision of the transcript read speakerId came from. Required. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[SpeakerIdentificationSubmittedResponse] - Identification accepted, pending review - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/speakers", - method="POST", - json={ - "speakerId": speaker_id, - "entityId": entity_id, - "name": name, - "revision": revision, - }, - headers={ - "content-type": "application/json", - "Idempotency-Key": str(idempotency_key) if idempotency_key is not None else None, - }, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - SpeakerIdentificationSubmittedResponse, - parse_obj_as( - type_=SpeakerIdentificationSubmittedResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 409: - raise ConflictError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) - - async def withdraw( - self, video_id: str, id: str, *, request_options: typing.Optional[RequestOptions] = None - ) -> AsyncHttpResponse[WithdrawnResponse]: - """ - Withdrawing also removes the community-attributed appearance the identification created. - - Parameters - ---------- - video_id : str - YouTube video id, 11 characters. - - id : str - The pending row id, as result.id of the response that accepted it. - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - AsyncHttpResponse[WithdrawnResponse] - Withdrawn - """ - _response = await self._client_wrapper.httpx_client.request( - f"v1/transcripts/{encode_path_param(video_id)}/speakers/{encode_path_param(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - _data = typing.cast( - WithdrawnResponse, - parse_obj_as( - type_=WithdrawnResponse, # type: ignore - object_=_response.json(), - ), - ) - return AsyncHttpResponse(response=_response, data=_data) - if _response.status_code == 400: - raise BadRequestError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 401: - raise UnauthorizedError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 403: - raise ForbiddenError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 404: - raise NotFoundError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 429: - raise TooManyRequestsError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - if _response.status_code == 500: - raise InternalServerError( - headers=dict(_response.headers), - body=typing.cast( - Error, - parse_obj_as( - type_=Error, # type: ignore - object_=_response.json(), - ), - ), - ) - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response.text) - except ValidationError as e: - raise ParsingError( - status_code=_response.status_code, headers=dict(_response.headers), body=_response.json(), cause=e - ) - raise ApiError(status_code=_response.status_code, headers=dict(_response.headers), body=_response_json) diff --git a/src/arcmira/types/__init__.py b/src/arcmira/types/__init__.py index a049749..3f16552 100644 --- a/src/arcmira/types/__init__.py +++ b/src/arcmira/types/__init__.py @@ -17,39 +17,19 @@ from .channel_coverage_response import ChannelCoverageResponse from .channel_coverage_response_channel import ChannelCoverageResponseChannel from .channel_coverage_response_channel_source_mix import ChannelCoverageResponseChannelSourceMix - from .channel_guest_list_response import ChannelGuestListResponse - from .channel_guest_list_response_export_capabilities import ChannelGuestListResponseExportCapabilities - from .channel_guest_list_response_items_item import ChannelGuestListResponseItemsItem - from .channel_guest_list_response_items_item_sentiment import ChannelGuestListResponseItemsItemSentiment - from .channel_page_response import ChannelPageResponse - from .channel_page_response_channel_info import ChannelPageResponseChannelInfo - from .channel_page_response_entity import ChannelPageResponseEntity - from .channel_page_response_entity_owner import ChannelPageResponseEntityOwner - from .channel_page_response_entity_type import ChannelPageResponseEntityType - from .channel_page_response_episodes_by_month_item import ChannelPageResponseEpisodesByMonthItem - from .channel_page_response_episodes_item import ChannelPageResponseEpisodesItem - from .channel_page_response_episodes_item_platform import ChannelPageResponseEpisodesItemPlatform - from .channel_page_response_episodes_item_sentiment import ChannelPageResponseEpisodesItemSentiment - from .channel_page_response_episodes_item_timestamp import ChannelPageResponseEpisodesItemTimestamp - from .channel_page_response_episodes_item_type import ChannelPageResponseEpisodesItemType - from .channel_page_response_guests_item import ChannelPageResponseGuestsItem - from .channel_page_response_guests_item_role import ChannelPageResponseGuestsItemRole - from .channel_page_response_guests_item_sentiment import ChannelPageResponseGuestsItemSentiment - from .channel_page_response_hosts_detailed_item import ChannelPageResponseHostsDetailedItem - from .channel_page_response_hosts_detailed_item_sentiment import ChannelPageResponseHostsDetailedItemSentiment - from .channel_page_response_organizations_item import ChannelPageResponseOrganizationsItem - from .channel_page_response_organizations_item_sentiment import ChannelPageResponseOrganizationsItemSentiment - from .channel_page_response_products_item import ChannelPageResponseProductsItem - from .channel_page_response_products_item_sentiment import ChannelPageResponseProductsItemSentiment - from .channel_page_response_recommendations_summary import ChannelPageResponseRecommendationsSummary - from .channel_page_response_stats import ChannelPageResponseStats - from .channel_page_response_topics_item import ChannelPageResponseTopicsItem - from .channel_page_response_topics_item_sentiment import ChannelPageResponseTopicsItemSentiment from .channel_sponsor import ChannelSponsor - from .channel_sponsor_entity import ChannelSponsorEntity from .channel_sponsor_sponsor_status import ChannelSponsorSponsorStatus from .channel_sponsors_response import ChannelSponsorsResponse from .channel_sponsors_response_access import ChannelSponsorsResponseAccess + from .channel_sponsors_response_access_details import ChannelSponsorsResponseAccessDetails + from .channel_sponsors_response_access_details_quote import ChannelSponsorsResponseAccessDetailsQuote + from .channel_sponsors_response_access_details_quote_charge import ChannelSponsorsResponseAccessDetailsQuoteCharge + from .channel_sponsors_response_access_details_quote_charge_from import ( + ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, + ) + from .channel_sponsors_response_access_details_quote_charge_unit import ( + ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, + ) from .channel_sponsors_response_access_gate import ChannelSponsorsResponseAccessGate from .channel_sponsors_response_access_reason import ChannelSponsorsResponseAccessReason from .channel_sponsors_response_access_type import ChannelSponsorsResponseAccessType @@ -60,21 +40,22 @@ from .channel_videos_response import ChannelVideosResponse from .channel_videos_response_channel import ChannelVideosResponseChannel from .channel_videos_response_episodes_item import ChannelVideosResponseEpisodesItem - from .correction_accepted_response import CorrectionAcceptedResponse - from .correction_accepted_response_kind import CorrectionAcceptedResponseKind from .delivery_issue_change import DeliveryIssueChange from .delivery_issue_change_channel import DeliveryIssueChangeChannel from .entity import Entity - from .entity_card import EntityCard - from .entity_cards_response import EntityCardsResponse - from .entity_channel_list_response import EntityChannelListResponse - from .entity_channel_list_response_export_capabilities import EntityChannelListResponseExportCapabilities - from .entity_channel_list_response_items_item import EntityChannelListResponseItemsItem from .entity_detail_recommendations_summary import EntityDetailRecommendationsSummary from .entity_detail_response import EntityDetailResponse - from .entity_lookup_response import EntityLookupResponse from .entity_momentum_response import EntityMomentumResponse from .entity_momentum_response_access import EntityMomentumResponseAccess + from .entity_momentum_response_access_details import EntityMomentumResponseAccessDetails + from .entity_momentum_response_access_details_quote import EntityMomentumResponseAccessDetailsQuote + from .entity_momentum_response_access_details_quote_charge import EntityMomentumResponseAccessDetailsQuoteCharge + from .entity_momentum_response_access_details_quote_charge_from import ( + EntityMomentumResponseAccessDetailsQuoteChargeFrom, + ) + from .entity_momentum_response_access_details_quote_charge_unit import ( + EntityMomentumResponseAccessDetailsQuoteChargeUnit, + ) from .entity_momentum_response_access_gate import EntityMomentumResponseAccessGate from .entity_momentum_response_access_reason import EntityMomentumResponseAccessReason from .entity_momentum_response_access_type import EntityMomentumResponseAccessType @@ -84,48 +65,23 @@ from .entity_momentum_response_top_shows_item import EntityMomentumResponseTopShowsItem from .entity_momentum_response_verdict import EntityMomentumResponseVerdict from .entity_momentum_response_volume import EntityMomentumResponseVolume - from .entity_organization_list_response import EntityOrganizationListResponse - from .entity_organization_list_response_export_capabilities import EntityOrganizationListResponseExportCapabilities - from .entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem - from .entity_organization_list_response_items_item_sentiment import EntityOrganizationListResponseItemsItemSentiment - from .entity_page_mention import EntityPageMention - from .entity_page_mention_excerpt import EntityPageMentionExcerpt - from .entity_page_mention_excerpt_public_source_class import EntityPageMentionExcerptPublicSourceClass - from .entity_page_mention_platform import EntityPageMentionPlatform - from .entity_page_mention_sentiment import EntityPageMentionSentiment - from .entity_page_mention_type import EntityPageMentionType - from .entity_people_list_response import EntityPeopleListResponse - from .entity_people_list_response_export_capabilities import EntityPeopleListResponseExportCapabilities - from .entity_people_list_response_items_item import EntityPeopleListResponseItemsItem - from .entity_people_list_response_items_item_sentiment import EntityPeopleListResponseItemsItemSentiment - from .entity_people_list_response_people_mode import EntityPeopleListResponsePeopleMode - from .entity_product_list_response import EntityProductListResponse - from .entity_product_list_response_export_capabilities import EntityProductListResponseExportCapabilities - from .entity_product_list_response_items_item import EntityProductListResponseItemsItem - from .entity_product_list_response_items_item_sentiment import EntityProductListResponseItemsItemSentiment from .entity_ref import EntityRef from .entity_resolve_response import EntityResolveResponse from .entity_resolve_response_ask import EntityResolveResponseAsk from .entity_resolve_response_ask_options_item import EntityResolveResponseAskOptionsItem from .entity_resolve_response_confidence import EntityResolveResponseConfidence - from .entity_search_response import EntitySearchResponse - from .entity_search_result import EntitySearchResult - from .entity_search_result_recommendations_summary import EntitySearchResultRecommendationsSummary - from .entity_topic_list_response import EntityTopicListResponse - from .entity_topic_list_response_export_capabilities import EntityTopicListResponseExportCapabilities - from .entity_topic_list_response_items_item import EntityTopicListResponseItemsItem - from .entity_topic_list_response_items_item_sentiment import EntityTopicListResponseItemsItemSentiment from .error import Error from .error_error import ErrorError + from .error_error_details import ErrorErrorDetails + from .error_error_details_quote import ErrorErrorDetailsQuote + from .error_error_details_quote_charge import ErrorErrorDetailsQuoteCharge + from .error_error_details_quote_charge_from import ErrorErrorDetailsQuoteChargeFrom + from .error_error_details_quote_charge_unit import ErrorErrorDetailsQuoteChargeUnit from .error_error_gate import ErrorErrorGate from .error_error_reason import ErrorErrorReason from .error_error_type import ErrorErrorType from .error_error_unlock import ErrorErrorUnlock from .error_error_unlock_action import ErrorErrorUnlockAction - from .error_quote import ErrorQuote - from .error_quote_charge import ErrorQuoteCharge - from .error_quote_charge_from import ErrorQuoteChargeFrom - from .error_quote_charge_unit import ErrorQuoteChargeUnit from .error_resource import ( ErrorResource, ErrorResource_Chart, @@ -160,49 +116,7 @@ from .error_resource_rows import ErrorResourceRows from .error_resource_sidebar_rows import ErrorResourceSidebarRows from .error_resource_sidebar_rows_section import ErrorResourceSidebarRowsSection - from .exposure_meta import ExposureMeta - from .exposure_meta_access import ExposureMetaAccess - from .exposure_meta_access_chart import ExposureMetaAccessChart - from .exposure_meta_access_cls import ExposureMetaAccessCls - from .exposure_meta_access_freshness import ExposureMetaAccessFreshness - from .exposure_meta_access_ladder import ExposureMetaAccessLadder - from .exposure_meta_access_rows import ExposureMetaAccessRows - from .exposure_meta_access_rows_entities import ExposureMetaAccessRowsEntities - from .exposure_meta_access_rows_media import ExposureMetaAccessRowsMedia - from .exposure_meta_access_rows_topics import ExposureMetaAccessRowsTopics - from .exposure_meta_access_unlock import ExposureMetaAccessUnlock - from .exposure_meta_access_unlock_limit_action import ExposureMetaAccessUnlockLimitAction - from .exposure_meta_access_unlock_src import ExposureMetaAccessUnlockSrc - from .exposure_meta_access_view import ExposureMetaAccessView - from .exposure_meta_access_withheld_item import ExposureMetaAccessWithheldItem - from .exposure_meta_access_withheld_item_kind import ExposureMetaAccessWithheldItemKind - from .exposure_meta_access_withheld_item_param import ExposureMetaAccessWithheldItemParam - from .exposure_meta_access_withheld_item_section import ExposureMetaAccessWithheldItemSection - from .exposure_meta_access_withheld_item_what import ExposureMetaAccessWithheldItemWhat - from .exposure_meta_credits import ExposureMetaCredits - from .exposure_meta_credits_on_demand import ExposureMetaCreditsOnDemand - from .exposure_meta_credits_plan import ExposureMetaCreditsPlan - from .exposure_meta_free_limit import ExposureMetaFreeLimit - from .exposure_meta_limit_action import ExposureMetaLimitAction - from .exposure_meta_limits import ExposureMetaLimits - from .exposure_meta_recent_preview import ExposureMetaRecentPreview - from .exposure_meta_recent_preview_experiment import ExposureMetaRecentPreviewExperiment - from .exposure_meta_recent_preview_mentions import ExposureMetaRecentPreviewMentions - from .exposure_meta_recent_preview_mentions_experiment import ExposureMetaRecentPreviewMentionsExperiment - from .exposure_meta_recent_preview_mentions_subject import ExposureMetaRecentPreviewMentionsSubject - from .exposure_meta_recent_preview_mentions_teaser_items_item import ( - ExposureMetaRecentPreviewMentionsTeaserItemsItem, - ) - from .exposure_meta_recent_preview_subject import ExposureMetaRecentPreviewSubject - from .exposure_meta_recent_preview_teaser_items_item import ExposureMetaRecentPreviewTeaserItemsItem - from .exposure_meta_totals import ExposureMetaTotals - from .exposure_meta_usage_limit_type import ExposureMetaUsageLimitType from .feedback_correction_result import FeedbackCorrectionResult - from .feedback_correction_result_recommendation import FeedbackCorrectionResultRecommendation - from .feedback_correction_result_recommendation_media import FeedbackCorrectionResultRecommendationMedia - from .feedback_correction_result_recommendation_media_source_channel import ( - FeedbackCorrectionResultRecommendationMediaSourceChannel, - ) from .feedback_correction_result_status import FeedbackCorrectionResultStatus from .feedback_readback_correction import FeedbackReadbackCorrection from .feedback_readback_correction_status import FeedbackReadbackCorrectionStatus @@ -228,7 +142,6 @@ from .mention_counts_response_shared_item import MentionCountsResponseSharedItem from .mention_counts_response_shared_item_by_channel_item import MentionCountsResponseSharedItemByChannelItem from .mention_list_response import MentionListResponse - from .mention_list_response_entity import MentionListResponseEntity from .mention_list_response_unlock import MentionListResponseUnlock from .mention_media import MentionMedia from .mention_media_source_channel import MentionMediaSourceChannel @@ -239,16 +152,22 @@ from .missed_alert_change import MissedAlertChange from .missing_result_change import MissingResultChange from .monitor import Monitor + from .monitor_access import MonitorAccess + from .monitor_add_entities_response import MonitorAddEntitiesResponse from .monitor_add_trackers_response import MonitorAddTrackersResponse from .monitor_delete_response import MonitorDeleteResponse from .monitor_email_recipients_item import MonitorEmailRecipientsItem from .monitor_email_recipients_item_invitation_status import MonitorEmailRecipientsItemInvitationStatus + from .monitor_email_recipients_item_role import MonitorEmailRecipientsItemRole from .monitor_email_recipients_item_status import MonitorEmailRecipientsItemStatus + from .monitor_entity_result import MonitorEntityResult + from .monitor_entity_result_reason import MonitorEntityResultReason from .monitor_list_response import MonitorListResponse from .monitor_list_response_monitors_item import MonitorListResponseMonitorsItem from .monitor_list_response_monitors_item_slack_integration import MonitorListResponseMonitorsItemSlackIntegration from .monitor_mutation_response import MonitorMutationResponse from .monitor_mutation_response_monitor import MonitorMutationResponseMonitor + from .monitor_team import MonitorTeam from .monitor_trackers_response import MonitorTrackersResponse from .monitor_trackers_response_trackers_item import MonitorTrackersResponseTrackersItem from .named_entity_ref import NamedEntityRef @@ -256,82 +175,12 @@ from .open_api_document_info import OpenApiDocumentInfo from .open_api_document_info_contact import OpenApiDocumentInfoContact from .open_api_document_servers_item import OpenApiDocumentServersItem - from .organization_page_response import OrganizationPageResponse - from .organization_page_response_channels_item import OrganizationPageResponseChannelsItem - from .organization_page_response_channels_item_sentiment import OrganizationPageResponseChannelsItemSentiment - from .organization_page_response_entity import OrganizationPageResponseEntity - from .organization_page_response_entity_owned_channels_item import OrganizationPageResponseEntityOwnedChannelsItem - from .organization_page_response_entity_owned_products_item import OrganizationPageResponseEntityOwnedProductsItem - from .organization_page_response_entity_type import OrganizationPageResponseEntityType - from .organization_page_response_mentions_by_month_item import OrganizationPageResponseMentionsByMonthItem - from .organization_page_response_people_item import OrganizationPageResponsePeopleItem - from .organization_page_response_people_item_sentiment import OrganizationPageResponsePeopleItemSentiment - from .organization_page_response_products_item import OrganizationPageResponseProductsItem - from .organization_page_response_products_item_sentiment import OrganizationPageResponseProductsItemSentiment - from .organization_page_response_role_edge import OrganizationPageResponseRoleEdge - from .organization_page_response_role_edge_label import OrganizationPageResponseRoleEdgeLabel - from .organization_page_response_role_edge_people_item import OrganizationPageResponseRoleEdgePeopleItem - from .organization_page_response_role_edge_recent_appearances_item import ( - OrganizationPageResponseRoleEdgeRecentAppearancesItem, - ) - from .organization_page_response_role_edge_role import OrganizationPageResponseRoleEdgeRole - from .organization_page_response_stats import OrganizationPageResponseStats - from .organization_page_response_topics_item import OrganizationPageResponseTopicsItem - from .organization_page_response_topics_item_sentiment import OrganizationPageResponseTopicsItemSentiment - from .person_appearance_list_response import PersonAppearanceListResponse - from .person_appearance_list_response_items_item import PersonAppearanceListResponseItemsItem - from .person_appearance_list_response_items_item_platform import PersonAppearanceListResponseItemsItemPlatform - from .person_appearance_list_response_items_item_sentiment import PersonAppearanceListResponseItemsItemSentiment - from .person_appearance_list_response_items_item_type import PersonAppearanceListResponseItemsItemType - from .person_page_response import PersonPageResponse - from .person_page_response_appearances_by_month_item import PersonPageResponseAppearancesByMonthItem - from .person_page_response_appearances_item import PersonPageResponseAppearancesItem - from .person_page_response_appearances_item_platform import PersonPageResponseAppearancesItemPlatform - from .person_page_response_appearances_item_sentiment import PersonPageResponseAppearancesItemSentiment - from .person_page_response_appearances_item_type import PersonPageResponseAppearancesItemType - from .person_page_response_brands_item import PersonPageResponseBrandsItem - from .person_page_response_brands_item_sentiment import PersonPageResponseBrandsItemSentiment - from .person_page_response_entity import PersonPageResponseEntity - from .person_page_response_entity_owned_channels_item import PersonPageResponseEntityOwnedChannelsItem - from .person_page_response_entity_owned_products_item import PersonPageResponseEntityOwnedProductsItem - from .person_page_response_mentions_by_month_item import PersonPageResponseMentionsByMonthItem - from .person_page_response_mentions_item import PersonPageResponseMentionsItem - from .person_page_response_mentions_item_platform import PersonPageResponseMentionsItemPlatform - from .person_page_response_mentions_item_sentiment import PersonPageResponseMentionsItemSentiment - from .person_page_response_mentions_item_type import PersonPageResponseMentionsItemType - from .person_page_response_people_item import PersonPageResponsePeopleItem - from .person_page_response_people_item_role import PersonPageResponsePeopleItemRole - from .person_page_response_people_item_sentiment import PersonPageResponsePeopleItemSentiment - from .person_page_response_products_item import PersonPageResponseProductsItem - from .person_page_response_products_item_sentiment import PersonPageResponseProductsItemSentiment - from .person_page_response_role_edge import PersonPageResponseRoleEdge - from .person_page_response_role_edge_label import PersonPageResponseRoleEdgeLabel - from .person_page_response_role_edge_role import PersonPageResponseRoleEdgeRole - from .person_page_response_stats import PersonPageResponseStats - from .person_page_response_topics_item import PersonPageResponseTopicsItem - from .person_page_response_topics_item_sentiment import PersonPageResponseTopicsItemSentiment - from .product_page_response import ProductPageResponse - from .product_page_response_channels_item import ProductPageResponseChannelsItem - from .product_page_response_channels_item_sentiment import ProductPageResponseChannelsItemSentiment - from .product_page_response_entity import ProductPageResponseEntity - from .product_page_response_entity_owner import ProductPageResponseEntityOwner - from .product_page_response_entity_parent_org import ProductPageResponseEntityParentOrg - from .product_page_response_entity_type import ProductPageResponseEntityType - from .product_page_response_mentions_by_month_item import ProductPageResponseMentionsByMonthItem - from .product_page_response_opportunities import ProductPageResponseOpportunities - from .product_page_response_organizations_item import ProductPageResponseOrganizationsItem - from .product_page_response_organizations_item_sentiment import ProductPageResponseOrganizationsItemSentiment - from .product_page_response_people_item import ProductPageResponsePeopleItem - from .product_page_response_people_item_sentiment import ProductPageResponsePeopleItemSentiment - from .product_page_response_stats import ProductPageResponseStats - from .product_page_response_topics_item import ProductPageResponseTopicsItem - from .product_page_response_topics_item_sentiment import ProductPageResponseTopicsItemSentiment - from .published_excerpt import PublishedExcerpt - from .published_excerpt_public_source_class import PublishedExcerptPublicSourceClass + from .publication_window import PublicationWindow from .recommendation import Recommendation + from .recommendation_class import RecommendationClass from .recommendation_enrichment_item import RecommendationEnrichmentItem + from .recommendation_enrichment_item_class import RecommendationEnrichmentItemClass from .recommendation_list_response import RecommendationListResponse - from .recommendation_list_response_entity import RecommendationListResponseEntity from .recommendation_media import RecommendationMedia from .recommendation_media_source_channel import RecommendationMediaSourceChannel from .resolve_candidate import ResolveCandidate @@ -339,56 +188,19 @@ from .resolve_suggestion import ResolveSuggestion from .resolve_suggestion_match import ResolveSuggestionMatch from .resolve_suggestion_reason import ResolveSuggestionReason - from .search_resolve_response import SearchResolveResponse - from .search_resolve_response_entity import SearchResolveResponseEntity from .signup_sent_response import SignupSentResponse from .signup_sent_response_next import SignupSentResponseNext from .signup_sent_response_next_method import SignupSentResponseNextMethod from .signup_verified_response import SignupVerifiedResponse - from .speaker_identification_submitted_response import SpeakerIdentificationSubmittedResponse - from .speaker_identification_submitted_response_identification import ( - SpeakerIdentificationSubmittedResponseIdentification, - ) - from .speaker_identification_submitted_response_identification_entity import ( - SpeakerIdentificationSubmittedResponseIdentificationEntity, - ) - from .speaker_identification_submitted_response_identification_status import ( - SpeakerIdentificationSubmittedResponseIdentificationStatus, + from .slack_integration_list_response import SlackIntegrationListResponse + from .slack_integration_list_response_integrations_item import SlackIntegrationListResponseIntegrationsItem + from .slack_integration_list_response_integrations_item_channels_item import ( + SlackIntegrationListResponseIntegrationsItemChannelsItem, ) from .stale_metadata_change import StaleMetadataChange - from .team_member import TeamMember - from .team_member_role import TeamMemberRole - from .team_member_seat_type import TeamMemberSeatType - from .team_member_spend import TeamMemberSpend - from .team_member_spend_role import TeamMemberSpendRole - from .team_member_spend_seat_type import TeamMemberSpendSeatType - from .team_members_response import TeamMembersResponse - from .team_members_response_team import TeamMembersResponseTeam - from .team_spend_response import TeamSpendResponse - from .team_usage_event import TeamUsageEvent - from .team_usage_events_response import TeamUsageEventsResponse - from .topic_page_response import TopicPageResponse - from .topic_page_response_channels_item import TopicPageResponseChannelsItem - from .topic_page_response_channels_item_sentiment import TopicPageResponseChannelsItemSentiment - from .topic_page_response_companies_item import TopicPageResponseCompaniesItem - from .topic_page_response_companies_item_sentiment import TopicPageResponseCompaniesItemSentiment - from .topic_page_response_entity import TopicPageResponseEntity - from .topic_page_response_entity_type import TopicPageResponseEntityType - from .topic_page_response_mentions_by_month_item import TopicPageResponseMentionsByMonthItem - from .topic_page_response_products_item import TopicPageResponseProductsItem - from .topic_page_response_products_item_sentiment import TopicPageResponseProductsItemSentiment - from .topic_page_response_related_topics_item import TopicPageResponseRelatedTopicsItem - from .topic_page_response_related_topics_item_sentiment import TopicPageResponseRelatedTopicsItemSentiment - from .topic_page_response_stats import TopicPageResponseStats - from .topic_page_response_voices_item import TopicPageResponseVoicesItem - from .topic_page_response_voices_item_role import TopicPageResponseVoicesItemRole - from .topic_page_response_voices_item_sentiment import TopicPageResponseVoicesItemSentiment from .tracker import Tracker from .tracker_list_response import TrackerListResponse from .tracker_mutation_response import TrackerMutationResponse - from .transcript_edit_submitted_response import TranscriptEditSubmittedResponse - from .transcript_edit_submitted_response_edit import TranscriptEditSubmittedResponseEdit - from .transcript_edit_submitted_response_edit_status import TranscriptEditSubmittedResponseEditStatus from .transcript_job import TranscriptJob from .transcript_job_charge import TranscriptJobCharge from .transcript_job_charge_from import TranscriptJobChargeFrom @@ -398,16 +210,6 @@ from .transcript_job_status import TranscriptJobStatus from .transcript_pending import TranscriptPending from .transcript_pending_quality import TranscriptPendingQuality - from .transcript_preparation_required import TranscriptPreparationRequired - from .transcript_preparation_required_action import TranscriptPreparationRequiredAction - from .transcript_preparation_required_action_body import TranscriptPreparationRequiredActionBody - from .transcript_preparation_required_action_method import TranscriptPreparationRequiredActionMethod - from .transcript_preparation_required_last_attempt import TranscriptPreparationRequiredLastAttempt - from .transcript_preparation_required_quality import TranscriptPreparationRequiredQuality - from .transcript_preparation_required_quote import TranscriptPreparationRequiredQuote - from .transcript_preparation_required_quote_charge import TranscriptPreparationRequiredQuoteCharge - from .transcript_preparation_required_quote_charge_from import TranscriptPreparationRequiredQuoteChargeFrom - from .transcript_preparation_required_quote_charge_unit import TranscriptPreparationRequiredQuoteChargeUnit from .transcript_purchase_quote import TranscriptPurchaseQuote from .transcript_purchase_quote_billing_scope import TranscriptPurchaseQuoteBillingScope from .transcript_purchase_quote_charge import TranscriptPurchaseQuoteCharge @@ -417,9 +219,13 @@ from .transcript_quote import TranscriptQuote from .transcript_request_list_response import TranscriptRequestListResponse from .transcript_request_list_response_requests_item import TranscriptRequestListResponseRequestsItem - from .transcript_request_submit_response import TranscriptRequestSubmitResponse from .transcript_response import TranscriptResponse from .transcript_response_access import TranscriptResponseAccess + from .transcript_response_access_details import TranscriptResponseAccessDetails + from .transcript_response_access_details_quote import TranscriptResponseAccessDetailsQuote + from .transcript_response_access_details_quote_charge import TranscriptResponseAccessDetailsQuoteCharge + from .transcript_response_access_details_quote_charge_from import TranscriptResponseAccessDetailsQuoteChargeFrom + from .transcript_response_access_details_quote_charge_unit import TranscriptResponseAccessDetailsQuoteChargeUnit from .transcript_response_access_gate import TranscriptResponseAccessGate from .transcript_response_access_reason import TranscriptResponseAccessReason from .transcript_response_access_type import TranscriptResponseAccessType @@ -431,37 +237,35 @@ from .transcript_response_range import TranscriptResponseRange from .transcript_response_source import TranscriptResponseSource from .transcript_response_speakers_item import TranscriptResponseSpeakersItem - from .transcript_result import ( - TranscriptResult, - TranscriptResult_Pending, - TranscriptResult_PreparationRequired, - TranscriptResult_Ready, - ) + from .transcript_result import TranscriptResult, TranscriptResult_Pending, TranscriptResult_Ready from .transcript_search_chunk import TranscriptSearchChunk from .transcript_search_response import TranscriptSearchResponse from .transcript_search_response_access import TranscriptSearchResponseAccess + from .transcript_search_response_access_details import TranscriptSearchResponseAccessDetails + from .transcript_search_response_access_details_quote import TranscriptSearchResponseAccessDetailsQuote + from .transcript_search_response_access_details_quote_charge import TranscriptSearchResponseAccessDetailsQuoteCharge + from .transcript_search_response_access_details_quote_charge_from import ( + TranscriptSearchResponseAccessDetailsQuoteChargeFrom, + ) + from .transcript_search_response_access_details_quote_charge_unit import ( + TranscriptSearchResponseAccessDetailsQuoteChargeUnit, + ) from .transcript_search_response_access_gate import TranscriptSearchResponseAccessGate from .transcript_search_response_access_reason import TranscriptSearchResponseAccessReason from .transcript_search_response_access_type import TranscriptSearchResponseAccessType from .transcript_search_response_access_unlock import TranscriptSearchResponseAccessUnlock from .transcript_search_response_access_unlock_action import TranscriptSearchResponseAccessUnlockAction from .transcript_search_response_filters import TranscriptSearchResponseFilters + from .transcript_search_response_filters_kind_item import TranscriptSearchResponseFiltersKindItem from .transcript_search_response_search_index import TranscriptSearchResponseSearchIndex from .transcript_search_response_search_index_state import TranscriptSearchResponseSearchIndexState + from .transcript_search_response_unlock import TranscriptSearchResponseUnlock from .transcript_settings import TranscriptSettings from .transcript_settings_quality import TranscriptSettingsQuality from .transcript_video import TranscriptVideo - from .video_captions_response import VideoCaptionsResponse - from .video_merge_list_response import VideoMergeListResponse - from .video_merge_list_response_merges_item import VideoMergeListResponseMergesItem - from .video_merge_list_response_merges_item_status import VideoMergeListResponseMergesItemStatus - from .video_merge_submitted_response import VideoMergeSubmittedResponse - from .video_merge_submitted_response_merge import VideoMergeSubmittedResponseMerge - from .video_merge_submitted_response_merge_status import VideoMergeSubmittedResponseMergeStatus from .webhook_secret_rotate_response import WebhookSecretRotateResponse - from .withdrawn_response import WithdrawnResponse from .wrong_classification_change import WrongClassificationChange - from .wrong_classification_change_mention_class import WrongClassificationChangeMentionClass + from .wrong_classification_change_class import WrongClassificationChangeClass from .wrong_entity_change import WrongEntityChange from .wrong_entity_type_change import WrongEntityTypeChange from .wrong_entity_type_change_field import WrongEntityTypeChangeField @@ -477,39 +281,15 @@ "ChannelCoverageResponse": ".channel_coverage_response", "ChannelCoverageResponseChannel": ".channel_coverage_response_channel", "ChannelCoverageResponseChannelSourceMix": ".channel_coverage_response_channel_source_mix", - "ChannelGuestListResponse": ".channel_guest_list_response", - "ChannelGuestListResponseExportCapabilities": ".channel_guest_list_response_export_capabilities", - "ChannelGuestListResponseItemsItem": ".channel_guest_list_response_items_item", - "ChannelGuestListResponseItemsItemSentiment": ".channel_guest_list_response_items_item_sentiment", - "ChannelPageResponse": ".channel_page_response", - "ChannelPageResponseChannelInfo": ".channel_page_response_channel_info", - "ChannelPageResponseEntity": ".channel_page_response_entity", - "ChannelPageResponseEntityOwner": ".channel_page_response_entity_owner", - "ChannelPageResponseEntityType": ".channel_page_response_entity_type", - "ChannelPageResponseEpisodesByMonthItem": ".channel_page_response_episodes_by_month_item", - "ChannelPageResponseEpisodesItem": ".channel_page_response_episodes_item", - "ChannelPageResponseEpisodesItemPlatform": ".channel_page_response_episodes_item_platform", - "ChannelPageResponseEpisodesItemSentiment": ".channel_page_response_episodes_item_sentiment", - "ChannelPageResponseEpisodesItemTimestamp": ".channel_page_response_episodes_item_timestamp", - "ChannelPageResponseEpisodesItemType": ".channel_page_response_episodes_item_type", - "ChannelPageResponseGuestsItem": ".channel_page_response_guests_item", - "ChannelPageResponseGuestsItemRole": ".channel_page_response_guests_item_role", - "ChannelPageResponseGuestsItemSentiment": ".channel_page_response_guests_item_sentiment", - "ChannelPageResponseHostsDetailedItem": ".channel_page_response_hosts_detailed_item", - "ChannelPageResponseHostsDetailedItemSentiment": ".channel_page_response_hosts_detailed_item_sentiment", - "ChannelPageResponseOrganizationsItem": ".channel_page_response_organizations_item", - "ChannelPageResponseOrganizationsItemSentiment": ".channel_page_response_organizations_item_sentiment", - "ChannelPageResponseProductsItem": ".channel_page_response_products_item", - "ChannelPageResponseProductsItemSentiment": ".channel_page_response_products_item_sentiment", - "ChannelPageResponseRecommendationsSummary": ".channel_page_response_recommendations_summary", - "ChannelPageResponseStats": ".channel_page_response_stats", - "ChannelPageResponseTopicsItem": ".channel_page_response_topics_item", - "ChannelPageResponseTopicsItemSentiment": ".channel_page_response_topics_item_sentiment", "ChannelSponsor": ".channel_sponsor", - "ChannelSponsorEntity": ".channel_sponsor_entity", "ChannelSponsorSponsorStatus": ".channel_sponsor_sponsor_status", "ChannelSponsorsResponse": ".channel_sponsors_response", "ChannelSponsorsResponseAccess": ".channel_sponsors_response_access", + "ChannelSponsorsResponseAccessDetails": ".channel_sponsors_response_access_details", + "ChannelSponsorsResponseAccessDetailsQuote": ".channel_sponsors_response_access_details_quote", + "ChannelSponsorsResponseAccessDetailsQuoteCharge": ".channel_sponsors_response_access_details_quote_charge", + "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom": ".channel_sponsors_response_access_details_quote_charge_from", + "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit": ".channel_sponsors_response_access_details_quote_charge_unit", "ChannelSponsorsResponseAccessGate": ".channel_sponsors_response_access_gate", "ChannelSponsorsResponseAccessReason": ".channel_sponsors_response_access_reason", "ChannelSponsorsResponseAccessType": ".channel_sponsors_response_access_type", @@ -520,21 +300,18 @@ "ChannelVideosResponse": ".channel_videos_response", "ChannelVideosResponseChannel": ".channel_videos_response_channel", "ChannelVideosResponseEpisodesItem": ".channel_videos_response_episodes_item", - "CorrectionAcceptedResponse": ".correction_accepted_response", - "CorrectionAcceptedResponseKind": ".correction_accepted_response_kind", "DeliveryIssueChange": ".delivery_issue_change", "DeliveryIssueChangeChannel": ".delivery_issue_change_channel", "Entity": ".entity", - "EntityCard": ".entity_card", - "EntityCardsResponse": ".entity_cards_response", - "EntityChannelListResponse": ".entity_channel_list_response", - "EntityChannelListResponseExportCapabilities": ".entity_channel_list_response_export_capabilities", - "EntityChannelListResponseItemsItem": ".entity_channel_list_response_items_item", "EntityDetailRecommendationsSummary": ".entity_detail_recommendations_summary", "EntityDetailResponse": ".entity_detail_response", - "EntityLookupResponse": ".entity_lookup_response", "EntityMomentumResponse": ".entity_momentum_response", "EntityMomentumResponseAccess": ".entity_momentum_response_access", + "EntityMomentumResponseAccessDetails": ".entity_momentum_response_access_details", + "EntityMomentumResponseAccessDetailsQuote": ".entity_momentum_response_access_details_quote", + "EntityMomentumResponseAccessDetailsQuoteCharge": ".entity_momentum_response_access_details_quote_charge", + "EntityMomentumResponseAccessDetailsQuoteChargeFrom": ".entity_momentum_response_access_details_quote_charge_from", + "EntityMomentumResponseAccessDetailsQuoteChargeUnit": ".entity_momentum_response_access_details_quote_charge_unit", "EntityMomentumResponseAccessGate": ".entity_momentum_response_access_gate", "EntityMomentumResponseAccessReason": ".entity_momentum_response_access_reason", "EntityMomentumResponseAccessType": ".entity_momentum_response_access_type", @@ -544,48 +321,23 @@ "EntityMomentumResponseTopShowsItem": ".entity_momentum_response_top_shows_item", "EntityMomentumResponseVerdict": ".entity_momentum_response_verdict", "EntityMomentumResponseVolume": ".entity_momentum_response_volume", - "EntityOrganizationListResponse": ".entity_organization_list_response", - "EntityOrganizationListResponseExportCapabilities": ".entity_organization_list_response_export_capabilities", - "EntityOrganizationListResponseItemsItem": ".entity_organization_list_response_items_item", - "EntityOrganizationListResponseItemsItemSentiment": ".entity_organization_list_response_items_item_sentiment", - "EntityPageMention": ".entity_page_mention", - "EntityPageMentionExcerpt": ".entity_page_mention_excerpt", - "EntityPageMentionExcerptPublicSourceClass": ".entity_page_mention_excerpt_public_source_class", - "EntityPageMentionPlatform": ".entity_page_mention_platform", - "EntityPageMentionSentiment": ".entity_page_mention_sentiment", - "EntityPageMentionType": ".entity_page_mention_type", - "EntityPeopleListResponse": ".entity_people_list_response", - "EntityPeopleListResponseExportCapabilities": ".entity_people_list_response_export_capabilities", - "EntityPeopleListResponseItemsItem": ".entity_people_list_response_items_item", - "EntityPeopleListResponseItemsItemSentiment": ".entity_people_list_response_items_item_sentiment", - "EntityPeopleListResponsePeopleMode": ".entity_people_list_response_people_mode", - "EntityProductListResponse": ".entity_product_list_response", - "EntityProductListResponseExportCapabilities": ".entity_product_list_response_export_capabilities", - "EntityProductListResponseItemsItem": ".entity_product_list_response_items_item", - "EntityProductListResponseItemsItemSentiment": ".entity_product_list_response_items_item_sentiment", "EntityRef": ".entity_ref", "EntityResolveResponse": ".entity_resolve_response", "EntityResolveResponseAsk": ".entity_resolve_response_ask", "EntityResolveResponseAskOptionsItem": ".entity_resolve_response_ask_options_item", "EntityResolveResponseConfidence": ".entity_resolve_response_confidence", - "EntitySearchResponse": ".entity_search_response", - "EntitySearchResult": ".entity_search_result", - "EntitySearchResultRecommendationsSummary": ".entity_search_result_recommendations_summary", - "EntityTopicListResponse": ".entity_topic_list_response", - "EntityTopicListResponseExportCapabilities": ".entity_topic_list_response_export_capabilities", - "EntityTopicListResponseItemsItem": ".entity_topic_list_response_items_item", - "EntityTopicListResponseItemsItemSentiment": ".entity_topic_list_response_items_item_sentiment", "Error": ".error", "ErrorError": ".error_error", + "ErrorErrorDetails": ".error_error_details", + "ErrorErrorDetailsQuote": ".error_error_details_quote", + "ErrorErrorDetailsQuoteCharge": ".error_error_details_quote_charge", + "ErrorErrorDetailsQuoteChargeFrom": ".error_error_details_quote_charge_from", + "ErrorErrorDetailsQuoteChargeUnit": ".error_error_details_quote_charge_unit", "ErrorErrorGate": ".error_error_gate", "ErrorErrorReason": ".error_error_reason", "ErrorErrorType": ".error_error_type", "ErrorErrorUnlock": ".error_error_unlock", "ErrorErrorUnlockAction": ".error_error_unlock_action", - "ErrorQuote": ".error_quote", - "ErrorQuoteCharge": ".error_quote_charge", - "ErrorQuoteChargeFrom": ".error_quote_charge_from", - "ErrorQuoteChargeUnit": ".error_quote_charge_unit", "ErrorResource": ".error_resource", "ErrorResourceChart": ".error_resource_chart", "ErrorResourceCommercial": ".error_resource_commercial", @@ -618,45 +370,7 @@ "ErrorResource_Requests": ".error_resource", "ErrorResource_Rows": ".error_resource", "ErrorResource_SidebarRows": ".error_resource", - "ExposureMeta": ".exposure_meta", - "ExposureMetaAccess": ".exposure_meta_access", - "ExposureMetaAccessChart": ".exposure_meta_access_chart", - "ExposureMetaAccessCls": ".exposure_meta_access_cls", - "ExposureMetaAccessFreshness": ".exposure_meta_access_freshness", - "ExposureMetaAccessLadder": ".exposure_meta_access_ladder", - "ExposureMetaAccessRows": ".exposure_meta_access_rows", - "ExposureMetaAccessRowsEntities": ".exposure_meta_access_rows_entities", - "ExposureMetaAccessRowsMedia": ".exposure_meta_access_rows_media", - "ExposureMetaAccessRowsTopics": ".exposure_meta_access_rows_topics", - "ExposureMetaAccessUnlock": ".exposure_meta_access_unlock", - "ExposureMetaAccessUnlockLimitAction": ".exposure_meta_access_unlock_limit_action", - "ExposureMetaAccessUnlockSrc": ".exposure_meta_access_unlock_src", - "ExposureMetaAccessView": ".exposure_meta_access_view", - "ExposureMetaAccessWithheldItem": ".exposure_meta_access_withheld_item", - "ExposureMetaAccessWithheldItemKind": ".exposure_meta_access_withheld_item_kind", - "ExposureMetaAccessWithheldItemParam": ".exposure_meta_access_withheld_item_param", - "ExposureMetaAccessWithheldItemSection": ".exposure_meta_access_withheld_item_section", - "ExposureMetaAccessWithheldItemWhat": ".exposure_meta_access_withheld_item_what", - "ExposureMetaCredits": ".exposure_meta_credits", - "ExposureMetaCreditsOnDemand": ".exposure_meta_credits_on_demand", - "ExposureMetaCreditsPlan": ".exposure_meta_credits_plan", - "ExposureMetaFreeLimit": ".exposure_meta_free_limit", - "ExposureMetaLimitAction": ".exposure_meta_limit_action", - "ExposureMetaLimits": ".exposure_meta_limits", - "ExposureMetaRecentPreview": ".exposure_meta_recent_preview", - "ExposureMetaRecentPreviewExperiment": ".exposure_meta_recent_preview_experiment", - "ExposureMetaRecentPreviewMentions": ".exposure_meta_recent_preview_mentions", - "ExposureMetaRecentPreviewMentionsExperiment": ".exposure_meta_recent_preview_mentions_experiment", - "ExposureMetaRecentPreviewMentionsSubject": ".exposure_meta_recent_preview_mentions_subject", - "ExposureMetaRecentPreviewMentionsTeaserItemsItem": ".exposure_meta_recent_preview_mentions_teaser_items_item", - "ExposureMetaRecentPreviewSubject": ".exposure_meta_recent_preview_subject", - "ExposureMetaRecentPreviewTeaserItemsItem": ".exposure_meta_recent_preview_teaser_items_item", - "ExposureMetaTotals": ".exposure_meta_totals", - "ExposureMetaUsageLimitType": ".exposure_meta_usage_limit_type", "FeedbackCorrectionResult": ".feedback_correction_result", - "FeedbackCorrectionResultRecommendation": ".feedback_correction_result_recommendation", - "FeedbackCorrectionResultRecommendationMedia": ".feedback_correction_result_recommendation_media", - "FeedbackCorrectionResultRecommendationMediaSourceChannel": ".feedback_correction_result_recommendation_media_source_channel", "FeedbackCorrectionResultStatus": ".feedback_correction_result_status", "FeedbackReadbackCorrection": ".feedback_readback_correction", "FeedbackReadbackCorrectionStatus": ".feedback_readback_correction_status", @@ -682,7 +396,6 @@ "MentionCountsResponseSharedItem": ".mention_counts_response_shared_item", "MentionCountsResponseSharedItemByChannelItem": ".mention_counts_response_shared_item_by_channel_item", "MentionListResponse": ".mention_list_response", - "MentionListResponseEntity": ".mention_list_response_entity", "MentionListResponseUnlock": ".mention_list_response_unlock", "MentionMedia": ".mention_media", "MentionMediaSourceChannel": ".mention_media_source_channel", @@ -693,16 +406,22 @@ "MissedAlertChange": ".missed_alert_change", "MissingResultChange": ".missing_result_change", "Monitor": ".monitor", + "MonitorAccess": ".monitor_access", + "MonitorAddEntitiesResponse": ".monitor_add_entities_response", "MonitorAddTrackersResponse": ".monitor_add_trackers_response", "MonitorDeleteResponse": ".monitor_delete_response", "MonitorEmailRecipientsItem": ".monitor_email_recipients_item", "MonitorEmailRecipientsItemInvitationStatus": ".monitor_email_recipients_item_invitation_status", + "MonitorEmailRecipientsItemRole": ".monitor_email_recipients_item_role", "MonitorEmailRecipientsItemStatus": ".monitor_email_recipients_item_status", + "MonitorEntityResult": ".monitor_entity_result", + "MonitorEntityResultReason": ".monitor_entity_result_reason", "MonitorListResponse": ".monitor_list_response", "MonitorListResponseMonitorsItem": ".monitor_list_response_monitors_item", "MonitorListResponseMonitorsItemSlackIntegration": ".monitor_list_response_monitors_item_slack_integration", "MonitorMutationResponse": ".monitor_mutation_response", "MonitorMutationResponseMonitor": ".monitor_mutation_response_monitor", + "MonitorTeam": ".monitor_team", "MonitorTrackersResponse": ".monitor_trackers_response", "MonitorTrackersResponseTrackersItem": ".monitor_trackers_response_trackers_item", "NamedEntityRef": ".named_entity_ref", @@ -710,80 +429,12 @@ "OpenApiDocumentInfo": ".open_api_document_info", "OpenApiDocumentInfoContact": ".open_api_document_info_contact", "OpenApiDocumentServersItem": ".open_api_document_servers_item", - "OrganizationPageResponse": ".organization_page_response", - "OrganizationPageResponseChannelsItem": ".organization_page_response_channels_item", - "OrganizationPageResponseChannelsItemSentiment": ".organization_page_response_channels_item_sentiment", - "OrganizationPageResponseEntity": ".organization_page_response_entity", - "OrganizationPageResponseEntityOwnedChannelsItem": ".organization_page_response_entity_owned_channels_item", - "OrganizationPageResponseEntityOwnedProductsItem": ".organization_page_response_entity_owned_products_item", - "OrganizationPageResponseEntityType": ".organization_page_response_entity_type", - "OrganizationPageResponseMentionsByMonthItem": ".organization_page_response_mentions_by_month_item", - "OrganizationPageResponsePeopleItem": ".organization_page_response_people_item", - "OrganizationPageResponsePeopleItemSentiment": ".organization_page_response_people_item_sentiment", - "OrganizationPageResponseProductsItem": ".organization_page_response_products_item", - "OrganizationPageResponseProductsItemSentiment": ".organization_page_response_products_item_sentiment", - "OrganizationPageResponseRoleEdge": ".organization_page_response_role_edge", - "OrganizationPageResponseRoleEdgeLabel": ".organization_page_response_role_edge_label", - "OrganizationPageResponseRoleEdgePeopleItem": ".organization_page_response_role_edge_people_item", - "OrganizationPageResponseRoleEdgeRecentAppearancesItem": ".organization_page_response_role_edge_recent_appearances_item", - "OrganizationPageResponseRoleEdgeRole": ".organization_page_response_role_edge_role", - "OrganizationPageResponseStats": ".organization_page_response_stats", - "OrganizationPageResponseTopicsItem": ".organization_page_response_topics_item", - "OrganizationPageResponseTopicsItemSentiment": ".organization_page_response_topics_item_sentiment", - "PersonAppearanceListResponse": ".person_appearance_list_response", - "PersonAppearanceListResponseItemsItem": ".person_appearance_list_response_items_item", - "PersonAppearanceListResponseItemsItemPlatform": ".person_appearance_list_response_items_item_platform", - "PersonAppearanceListResponseItemsItemSentiment": ".person_appearance_list_response_items_item_sentiment", - "PersonAppearanceListResponseItemsItemType": ".person_appearance_list_response_items_item_type", - "PersonPageResponse": ".person_page_response", - "PersonPageResponseAppearancesByMonthItem": ".person_page_response_appearances_by_month_item", - "PersonPageResponseAppearancesItem": ".person_page_response_appearances_item", - "PersonPageResponseAppearancesItemPlatform": ".person_page_response_appearances_item_platform", - "PersonPageResponseAppearancesItemSentiment": ".person_page_response_appearances_item_sentiment", - "PersonPageResponseAppearancesItemType": ".person_page_response_appearances_item_type", - "PersonPageResponseBrandsItem": ".person_page_response_brands_item", - "PersonPageResponseBrandsItemSentiment": ".person_page_response_brands_item_sentiment", - "PersonPageResponseEntity": ".person_page_response_entity", - "PersonPageResponseEntityOwnedChannelsItem": ".person_page_response_entity_owned_channels_item", - "PersonPageResponseEntityOwnedProductsItem": ".person_page_response_entity_owned_products_item", - "PersonPageResponseMentionsByMonthItem": ".person_page_response_mentions_by_month_item", - "PersonPageResponseMentionsItem": ".person_page_response_mentions_item", - "PersonPageResponseMentionsItemPlatform": ".person_page_response_mentions_item_platform", - "PersonPageResponseMentionsItemSentiment": ".person_page_response_mentions_item_sentiment", - "PersonPageResponseMentionsItemType": ".person_page_response_mentions_item_type", - "PersonPageResponsePeopleItem": ".person_page_response_people_item", - "PersonPageResponsePeopleItemRole": ".person_page_response_people_item_role", - "PersonPageResponsePeopleItemSentiment": ".person_page_response_people_item_sentiment", - "PersonPageResponseProductsItem": ".person_page_response_products_item", - "PersonPageResponseProductsItemSentiment": ".person_page_response_products_item_sentiment", - "PersonPageResponseRoleEdge": ".person_page_response_role_edge", - "PersonPageResponseRoleEdgeLabel": ".person_page_response_role_edge_label", - "PersonPageResponseRoleEdgeRole": ".person_page_response_role_edge_role", - "PersonPageResponseStats": ".person_page_response_stats", - "PersonPageResponseTopicsItem": ".person_page_response_topics_item", - "PersonPageResponseTopicsItemSentiment": ".person_page_response_topics_item_sentiment", - "ProductPageResponse": ".product_page_response", - "ProductPageResponseChannelsItem": ".product_page_response_channels_item", - "ProductPageResponseChannelsItemSentiment": ".product_page_response_channels_item_sentiment", - "ProductPageResponseEntity": ".product_page_response_entity", - "ProductPageResponseEntityOwner": ".product_page_response_entity_owner", - "ProductPageResponseEntityParentOrg": ".product_page_response_entity_parent_org", - "ProductPageResponseEntityType": ".product_page_response_entity_type", - "ProductPageResponseMentionsByMonthItem": ".product_page_response_mentions_by_month_item", - "ProductPageResponseOpportunities": ".product_page_response_opportunities", - "ProductPageResponseOrganizationsItem": ".product_page_response_organizations_item", - "ProductPageResponseOrganizationsItemSentiment": ".product_page_response_organizations_item_sentiment", - "ProductPageResponsePeopleItem": ".product_page_response_people_item", - "ProductPageResponsePeopleItemSentiment": ".product_page_response_people_item_sentiment", - "ProductPageResponseStats": ".product_page_response_stats", - "ProductPageResponseTopicsItem": ".product_page_response_topics_item", - "ProductPageResponseTopicsItemSentiment": ".product_page_response_topics_item_sentiment", - "PublishedExcerpt": ".published_excerpt", - "PublishedExcerptPublicSourceClass": ".published_excerpt_public_source_class", + "PublicationWindow": ".publication_window", "Recommendation": ".recommendation", + "RecommendationClass": ".recommendation_class", "RecommendationEnrichmentItem": ".recommendation_enrichment_item", + "RecommendationEnrichmentItemClass": ".recommendation_enrichment_item_class", "RecommendationListResponse": ".recommendation_list_response", - "RecommendationListResponseEntity": ".recommendation_list_response_entity", "RecommendationMedia": ".recommendation_media", "RecommendationMediaSourceChannel": ".recommendation_media_source_channel", "ResolveCandidate": ".resolve_candidate", @@ -791,50 +442,17 @@ "ResolveSuggestion": ".resolve_suggestion", "ResolveSuggestionMatch": ".resolve_suggestion_match", "ResolveSuggestionReason": ".resolve_suggestion_reason", - "SearchResolveResponse": ".search_resolve_response", - "SearchResolveResponseEntity": ".search_resolve_response_entity", "SignupSentResponse": ".signup_sent_response", "SignupSentResponseNext": ".signup_sent_response_next", "SignupSentResponseNextMethod": ".signup_sent_response_next_method", "SignupVerifiedResponse": ".signup_verified_response", - "SpeakerIdentificationSubmittedResponse": ".speaker_identification_submitted_response", - "SpeakerIdentificationSubmittedResponseIdentification": ".speaker_identification_submitted_response_identification", - "SpeakerIdentificationSubmittedResponseIdentificationEntity": ".speaker_identification_submitted_response_identification_entity", - "SpeakerIdentificationSubmittedResponseIdentificationStatus": ".speaker_identification_submitted_response_identification_status", + "SlackIntegrationListResponse": ".slack_integration_list_response", + "SlackIntegrationListResponseIntegrationsItem": ".slack_integration_list_response_integrations_item", + "SlackIntegrationListResponseIntegrationsItemChannelsItem": ".slack_integration_list_response_integrations_item_channels_item", "StaleMetadataChange": ".stale_metadata_change", - "TeamMember": ".team_member", - "TeamMemberRole": ".team_member_role", - "TeamMemberSeatType": ".team_member_seat_type", - "TeamMemberSpend": ".team_member_spend", - "TeamMemberSpendRole": ".team_member_spend_role", - "TeamMemberSpendSeatType": ".team_member_spend_seat_type", - "TeamMembersResponse": ".team_members_response", - "TeamMembersResponseTeam": ".team_members_response_team", - "TeamSpendResponse": ".team_spend_response", - "TeamUsageEvent": ".team_usage_event", - "TeamUsageEventsResponse": ".team_usage_events_response", - "TopicPageResponse": ".topic_page_response", - "TopicPageResponseChannelsItem": ".topic_page_response_channels_item", - "TopicPageResponseChannelsItemSentiment": ".topic_page_response_channels_item_sentiment", - "TopicPageResponseCompaniesItem": ".topic_page_response_companies_item", - "TopicPageResponseCompaniesItemSentiment": ".topic_page_response_companies_item_sentiment", - "TopicPageResponseEntity": ".topic_page_response_entity", - "TopicPageResponseEntityType": ".topic_page_response_entity_type", - "TopicPageResponseMentionsByMonthItem": ".topic_page_response_mentions_by_month_item", - "TopicPageResponseProductsItem": ".topic_page_response_products_item", - "TopicPageResponseProductsItemSentiment": ".topic_page_response_products_item_sentiment", - "TopicPageResponseRelatedTopicsItem": ".topic_page_response_related_topics_item", - "TopicPageResponseRelatedTopicsItemSentiment": ".topic_page_response_related_topics_item_sentiment", - "TopicPageResponseStats": ".topic_page_response_stats", - "TopicPageResponseVoicesItem": ".topic_page_response_voices_item", - "TopicPageResponseVoicesItemRole": ".topic_page_response_voices_item_role", - "TopicPageResponseVoicesItemSentiment": ".topic_page_response_voices_item_sentiment", "Tracker": ".tracker", "TrackerListResponse": ".tracker_list_response", "TrackerMutationResponse": ".tracker_mutation_response", - "TranscriptEditSubmittedResponse": ".transcript_edit_submitted_response", - "TranscriptEditSubmittedResponseEdit": ".transcript_edit_submitted_response_edit", - "TranscriptEditSubmittedResponseEditStatus": ".transcript_edit_submitted_response_edit_status", "TranscriptJob": ".transcript_job", "TranscriptJobCharge": ".transcript_job_charge", "TranscriptJobChargeFrom": ".transcript_job_charge_from", @@ -844,16 +462,6 @@ "TranscriptJobStatus": ".transcript_job_status", "TranscriptPending": ".transcript_pending", "TranscriptPendingQuality": ".transcript_pending_quality", - "TranscriptPreparationRequired": ".transcript_preparation_required", - "TranscriptPreparationRequiredAction": ".transcript_preparation_required_action", - "TranscriptPreparationRequiredActionBody": ".transcript_preparation_required_action_body", - "TranscriptPreparationRequiredActionMethod": ".transcript_preparation_required_action_method", - "TranscriptPreparationRequiredLastAttempt": ".transcript_preparation_required_last_attempt", - "TranscriptPreparationRequiredQuality": ".transcript_preparation_required_quality", - "TranscriptPreparationRequiredQuote": ".transcript_preparation_required_quote", - "TranscriptPreparationRequiredQuoteCharge": ".transcript_preparation_required_quote_charge", - "TranscriptPreparationRequiredQuoteChargeFrom": ".transcript_preparation_required_quote_charge_from", - "TranscriptPreparationRequiredQuoteChargeUnit": ".transcript_preparation_required_quote_charge_unit", "TranscriptPurchaseQuote": ".transcript_purchase_quote", "TranscriptPurchaseQuoteBillingScope": ".transcript_purchase_quote_billing_scope", "TranscriptPurchaseQuoteCharge": ".transcript_purchase_quote_charge", @@ -863,9 +471,13 @@ "TranscriptQuote": ".transcript_quote", "TranscriptRequestListResponse": ".transcript_request_list_response", "TranscriptRequestListResponseRequestsItem": ".transcript_request_list_response_requests_item", - "TranscriptRequestSubmitResponse": ".transcript_request_submit_response", "TranscriptResponse": ".transcript_response", "TranscriptResponseAccess": ".transcript_response_access", + "TranscriptResponseAccessDetails": ".transcript_response_access_details", + "TranscriptResponseAccessDetailsQuote": ".transcript_response_access_details_quote", + "TranscriptResponseAccessDetailsQuoteCharge": ".transcript_response_access_details_quote_charge", + "TranscriptResponseAccessDetailsQuoteChargeFrom": ".transcript_response_access_details_quote_charge_from", + "TranscriptResponseAccessDetailsQuoteChargeUnit": ".transcript_response_access_details_quote_charge_unit", "TranscriptResponseAccessGate": ".transcript_response_access_gate", "TranscriptResponseAccessReason": ".transcript_response_access_reason", "TranscriptResponseAccessType": ".transcript_response_access_type", @@ -879,33 +491,31 @@ "TranscriptResponseSpeakersItem": ".transcript_response_speakers_item", "TranscriptResult": ".transcript_result", "TranscriptResult_Pending": ".transcript_result", - "TranscriptResult_PreparationRequired": ".transcript_result", "TranscriptResult_Ready": ".transcript_result", "TranscriptSearchChunk": ".transcript_search_chunk", "TranscriptSearchResponse": ".transcript_search_response", "TranscriptSearchResponseAccess": ".transcript_search_response_access", + "TranscriptSearchResponseAccessDetails": ".transcript_search_response_access_details", + "TranscriptSearchResponseAccessDetailsQuote": ".transcript_search_response_access_details_quote", + "TranscriptSearchResponseAccessDetailsQuoteCharge": ".transcript_search_response_access_details_quote_charge", + "TranscriptSearchResponseAccessDetailsQuoteChargeFrom": ".transcript_search_response_access_details_quote_charge_from", + "TranscriptSearchResponseAccessDetailsQuoteChargeUnit": ".transcript_search_response_access_details_quote_charge_unit", "TranscriptSearchResponseAccessGate": ".transcript_search_response_access_gate", "TranscriptSearchResponseAccessReason": ".transcript_search_response_access_reason", "TranscriptSearchResponseAccessType": ".transcript_search_response_access_type", "TranscriptSearchResponseAccessUnlock": ".transcript_search_response_access_unlock", "TranscriptSearchResponseAccessUnlockAction": ".transcript_search_response_access_unlock_action", "TranscriptSearchResponseFilters": ".transcript_search_response_filters", + "TranscriptSearchResponseFiltersKindItem": ".transcript_search_response_filters_kind_item", "TranscriptSearchResponseSearchIndex": ".transcript_search_response_search_index", "TranscriptSearchResponseSearchIndexState": ".transcript_search_response_search_index_state", + "TranscriptSearchResponseUnlock": ".transcript_search_response_unlock", "TranscriptSettings": ".transcript_settings", "TranscriptSettingsQuality": ".transcript_settings_quality", "TranscriptVideo": ".transcript_video", - "VideoCaptionsResponse": ".video_captions_response", - "VideoMergeListResponse": ".video_merge_list_response", - "VideoMergeListResponseMergesItem": ".video_merge_list_response_merges_item", - "VideoMergeListResponseMergesItemStatus": ".video_merge_list_response_merges_item_status", - "VideoMergeSubmittedResponse": ".video_merge_submitted_response", - "VideoMergeSubmittedResponseMerge": ".video_merge_submitted_response_merge", - "VideoMergeSubmittedResponseMergeStatus": ".video_merge_submitted_response_merge_status", "WebhookSecretRotateResponse": ".webhook_secret_rotate_response", - "WithdrawnResponse": ".withdrawn_response", "WrongClassificationChange": ".wrong_classification_change", - "WrongClassificationChangeMentionClass": ".wrong_classification_change_mention_class", + "WrongClassificationChangeClass": ".wrong_classification_change_class", "WrongEntityChange": ".wrong_entity_change", "WrongEntityTypeChange": ".wrong_entity_type_change", "WrongEntityTypeChangeField": ".wrong_entity_type_change_field", @@ -945,39 +555,15 @@ def __dir__(): "ChannelCoverageResponse", "ChannelCoverageResponseChannel", "ChannelCoverageResponseChannelSourceMix", - "ChannelGuestListResponse", - "ChannelGuestListResponseExportCapabilities", - "ChannelGuestListResponseItemsItem", - "ChannelGuestListResponseItemsItemSentiment", - "ChannelPageResponse", - "ChannelPageResponseChannelInfo", - "ChannelPageResponseEntity", - "ChannelPageResponseEntityOwner", - "ChannelPageResponseEntityType", - "ChannelPageResponseEpisodesByMonthItem", - "ChannelPageResponseEpisodesItem", - "ChannelPageResponseEpisodesItemPlatform", - "ChannelPageResponseEpisodesItemSentiment", - "ChannelPageResponseEpisodesItemTimestamp", - "ChannelPageResponseEpisodesItemType", - "ChannelPageResponseGuestsItem", - "ChannelPageResponseGuestsItemRole", - "ChannelPageResponseGuestsItemSentiment", - "ChannelPageResponseHostsDetailedItem", - "ChannelPageResponseHostsDetailedItemSentiment", - "ChannelPageResponseOrganizationsItem", - "ChannelPageResponseOrganizationsItemSentiment", - "ChannelPageResponseProductsItem", - "ChannelPageResponseProductsItemSentiment", - "ChannelPageResponseRecommendationsSummary", - "ChannelPageResponseStats", - "ChannelPageResponseTopicsItem", - "ChannelPageResponseTopicsItemSentiment", "ChannelSponsor", - "ChannelSponsorEntity", "ChannelSponsorSponsorStatus", "ChannelSponsorsResponse", "ChannelSponsorsResponseAccess", + "ChannelSponsorsResponseAccessDetails", + "ChannelSponsorsResponseAccessDetailsQuote", + "ChannelSponsorsResponseAccessDetailsQuoteCharge", + "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom", + "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit", "ChannelSponsorsResponseAccessGate", "ChannelSponsorsResponseAccessReason", "ChannelSponsorsResponseAccessType", @@ -988,21 +574,18 @@ def __dir__(): "ChannelVideosResponse", "ChannelVideosResponseChannel", "ChannelVideosResponseEpisodesItem", - "CorrectionAcceptedResponse", - "CorrectionAcceptedResponseKind", "DeliveryIssueChange", "DeliveryIssueChangeChannel", "Entity", - "EntityCard", - "EntityCardsResponse", - "EntityChannelListResponse", - "EntityChannelListResponseExportCapabilities", - "EntityChannelListResponseItemsItem", "EntityDetailRecommendationsSummary", "EntityDetailResponse", - "EntityLookupResponse", "EntityMomentumResponse", "EntityMomentumResponseAccess", + "EntityMomentumResponseAccessDetails", + "EntityMomentumResponseAccessDetailsQuote", + "EntityMomentumResponseAccessDetailsQuoteCharge", + "EntityMomentumResponseAccessDetailsQuoteChargeFrom", + "EntityMomentumResponseAccessDetailsQuoteChargeUnit", "EntityMomentumResponseAccessGate", "EntityMomentumResponseAccessReason", "EntityMomentumResponseAccessType", @@ -1012,48 +595,23 @@ def __dir__(): "EntityMomentumResponseTopShowsItem", "EntityMomentumResponseVerdict", "EntityMomentumResponseVolume", - "EntityOrganizationListResponse", - "EntityOrganizationListResponseExportCapabilities", - "EntityOrganizationListResponseItemsItem", - "EntityOrganizationListResponseItemsItemSentiment", - "EntityPageMention", - "EntityPageMentionExcerpt", - "EntityPageMentionExcerptPublicSourceClass", - "EntityPageMentionPlatform", - "EntityPageMentionSentiment", - "EntityPageMentionType", - "EntityPeopleListResponse", - "EntityPeopleListResponseExportCapabilities", - "EntityPeopleListResponseItemsItem", - "EntityPeopleListResponseItemsItemSentiment", - "EntityPeopleListResponsePeopleMode", - "EntityProductListResponse", - "EntityProductListResponseExportCapabilities", - "EntityProductListResponseItemsItem", - "EntityProductListResponseItemsItemSentiment", "EntityRef", "EntityResolveResponse", "EntityResolveResponseAsk", "EntityResolveResponseAskOptionsItem", "EntityResolveResponseConfidence", - "EntitySearchResponse", - "EntitySearchResult", - "EntitySearchResultRecommendationsSummary", - "EntityTopicListResponse", - "EntityTopicListResponseExportCapabilities", - "EntityTopicListResponseItemsItem", - "EntityTopicListResponseItemsItemSentiment", "Error", "ErrorError", + "ErrorErrorDetails", + "ErrorErrorDetailsQuote", + "ErrorErrorDetailsQuoteCharge", + "ErrorErrorDetailsQuoteChargeFrom", + "ErrorErrorDetailsQuoteChargeUnit", "ErrorErrorGate", "ErrorErrorReason", "ErrorErrorType", "ErrorErrorUnlock", "ErrorErrorUnlockAction", - "ErrorQuote", - "ErrorQuoteCharge", - "ErrorQuoteChargeFrom", - "ErrorQuoteChargeUnit", "ErrorResource", "ErrorResourceChart", "ErrorResourceCommercial", @@ -1086,45 +644,7 @@ def __dir__(): "ErrorResource_Requests", "ErrorResource_Rows", "ErrorResource_SidebarRows", - "ExposureMeta", - "ExposureMetaAccess", - "ExposureMetaAccessChart", - "ExposureMetaAccessCls", - "ExposureMetaAccessFreshness", - "ExposureMetaAccessLadder", - "ExposureMetaAccessRows", - "ExposureMetaAccessRowsEntities", - "ExposureMetaAccessRowsMedia", - "ExposureMetaAccessRowsTopics", - "ExposureMetaAccessUnlock", - "ExposureMetaAccessUnlockLimitAction", - "ExposureMetaAccessUnlockSrc", - "ExposureMetaAccessView", - "ExposureMetaAccessWithheldItem", - "ExposureMetaAccessWithheldItemKind", - "ExposureMetaAccessWithheldItemParam", - "ExposureMetaAccessWithheldItemSection", - "ExposureMetaAccessWithheldItemWhat", - "ExposureMetaCredits", - "ExposureMetaCreditsOnDemand", - "ExposureMetaCreditsPlan", - "ExposureMetaFreeLimit", - "ExposureMetaLimitAction", - "ExposureMetaLimits", - "ExposureMetaRecentPreview", - "ExposureMetaRecentPreviewExperiment", - "ExposureMetaRecentPreviewMentions", - "ExposureMetaRecentPreviewMentionsExperiment", - "ExposureMetaRecentPreviewMentionsSubject", - "ExposureMetaRecentPreviewMentionsTeaserItemsItem", - "ExposureMetaRecentPreviewSubject", - "ExposureMetaRecentPreviewTeaserItemsItem", - "ExposureMetaTotals", - "ExposureMetaUsageLimitType", "FeedbackCorrectionResult", - "FeedbackCorrectionResultRecommendation", - "FeedbackCorrectionResultRecommendationMedia", - "FeedbackCorrectionResultRecommendationMediaSourceChannel", "FeedbackCorrectionResultStatus", "FeedbackReadbackCorrection", "FeedbackReadbackCorrectionStatus", @@ -1150,7 +670,6 @@ def __dir__(): "MentionCountsResponseSharedItem", "MentionCountsResponseSharedItemByChannelItem", "MentionListResponse", - "MentionListResponseEntity", "MentionListResponseUnlock", "MentionMedia", "MentionMediaSourceChannel", @@ -1161,16 +680,22 @@ def __dir__(): "MissedAlertChange", "MissingResultChange", "Monitor", + "MonitorAccess", + "MonitorAddEntitiesResponse", "MonitorAddTrackersResponse", "MonitorDeleteResponse", "MonitorEmailRecipientsItem", "MonitorEmailRecipientsItemInvitationStatus", + "MonitorEmailRecipientsItemRole", "MonitorEmailRecipientsItemStatus", + "MonitorEntityResult", + "MonitorEntityResultReason", "MonitorListResponse", "MonitorListResponseMonitorsItem", "MonitorListResponseMonitorsItemSlackIntegration", "MonitorMutationResponse", "MonitorMutationResponseMonitor", + "MonitorTeam", "MonitorTrackersResponse", "MonitorTrackersResponseTrackersItem", "NamedEntityRef", @@ -1178,80 +703,12 @@ def __dir__(): "OpenApiDocumentInfo", "OpenApiDocumentInfoContact", "OpenApiDocumentServersItem", - "OrganizationPageResponse", - "OrganizationPageResponseChannelsItem", - "OrganizationPageResponseChannelsItemSentiment", - "OrganizationPageResponseEntity", - "OrganizationPageResponseEntityOwnedChannelsItem", - "OrganizationPageResponseEntityOwnedProductsItem", - "OrganizationPageResponseEntityType", - "OrganizationPageResponseMentionsByMonthItem", - "OrganizationPageResponsePeopleItem", - "OrganizationPageResponsePeopleItemSentiment", - "OrganizationPageResponseProductsItem", - "OrganizationPageResponseProductsItemSentiment", - "OrganizationPageResponseRoleEdge", - "OrganizationPageResponseRoleEdgeLabel", - "OrganizationPageResponseRoleEdgePeopleItem", - "OrganizationPageResponseRoleEdgeRecentAppearancesItem", - "OrganizationPageResponseRoleEdgeRole", - "OrganizationPageResponseStats", - "OrganizationPageResponseTopicsItem", - "OrganizationPageResponseTopicsItemSentiment", - "PersonAppearanceListResponse", - "PersonAppearanceListResponseItemsItem", - "PersonAppearanceListResponseItemsItemPlatform", - "PersonAppearanceListResponseItemsItemSentiment", - "PersonAppearanceListResponseItemsItemType", - "PersonPageResponse", - "PersonPageResponseAppearancesByMonthItem", - "PersonPageResponseAppearancesItem", - "PersonPageResponseAppearancesItemPlatform", - "PersonPageResponseAppearancesItemSentiment", - "PersonPageResponseAppearancesItemType", - "PersonPageResponseBrandsItem", - "PersonPageResponseBrandsItemSentiment", - "PersonPageResponseEntity", - "PersonPageResponseEntityOwnedChannelsItem", - "PersonPageResponseEntityOwnedProductsItem", - "PersonPageResponseMentionsByMonthItem", - "PersonPageResponseMentionsItem", - "PersonPageResponseMentionsItemPlatform", - "PersonPageResponseMentionsItemSentiment", - "PersonPageResponseMentionsItemType", - "PersonPageResponsePeopleItem", - "PersonPageResponsePeopleItemRole", - "PersonPageResponsePeopleItemSentiment", - "PersonPageResponseProductsItem", - "PersonPageResponseProductsItemSentiment", - "PersonPageResponseRoleEdge", - "PersonPageResponseRoleEdgeLabel", - "PersonPageResponseRoleEdgeRole", - "PersonPageResponseStats", - "PersonPageResponseTopicsItem", - "PersonPageResponseTopicsItemSentiment", - "ProductPageResponse", - "ProductPageResponseChannelsItem", - "ProductPageResponseChannelsItemSentiment", - "ProductPageResponseEntity", - "ProductPageResponseEntityOwner", - "ProductPageResponseEntityParentOrg", - "ProductPageResponseEntityType", - "ProductPageResponseMentionsByMonthItem", - "ProductPageResponseOpportunities", - "ProductPageResponseOrganizationsItem", - "ProductPageResponseOrganizationsItemSentiment", - "ProductPageResponsePeopleItem", - "ProductPageResponsePeopleItemSentiment", - "ProductPageResponseStats", - "ProductPageResponseTopicsItem", - "ProductPageResponseTopicsItemSentiment", - "PublishedExcerpt", - "PublishedExcerptPublicSourceClass", + "PublicationWindow", "Recommendation", + "RecommendationClass", "RecommendationEnrichmentItem", + "RecommendationEnrichmentItemClass", "RecommendationListResponse", - "RecommendationListResponseEntity", "RecommendationMedia", "RecommendationMediaSourceChannel", "ResolveCandidate", @@ -1259,50 +716,17 @@ def __dir__(): "ResolveSuggestion", "ResolveSuggestionMatch", "ResolveSuggestionReason", - "SearchResolveResponse", - "SearchResolveResponseEntity", "SignupSentResponse", "SignupSentResponseNext", "SignupSentResponseNextMethod", "SignupVerifiedResponse", - "SpeakerIdentificationSubmittedResponse", - "SpeakerIdentificationSubmittedResponseIdentification", - "SpeakerIdentificationSubmittedResponseIdentificationEntity", - "SpeakerIdentificationSubmittedResponseIdentificationStatus", + "SlackIntegrationListResponse", + "SlackIntegrationListResponseIntegrationsItem", + "SlackIntegrationListResponseIntegrationsItemChannelsItem", "StaleMetadataChange", - "TeamMember", - "TeamMemberRole", - "TeamMemberSeatType", - "TeamMemberSpend", - "TeamMemberSpendRole", - "TeamMemberSpendSeatType", - "TeamMembersResponse", - "TeamMembersResponseTeam", - "TeamSpendResponse", - "TeamUsageEvent", - "TeamUsageEventsResponse", - "TopicPageResponse", - "TopicPageResponseChannelsItem", - "TopicPageResponseChannelsItemSentiment", - "TopicPageResponseCompaniesItem", - "TopicPageResponseCompaniesItemSentiment", - "TopicPageResponseEntity", - "TopicPageResponseEntityType", - "TopicPageResponseMentionsByMonthItem", - "TopicPageResponseProductsItem", - "TopicPageResponseProductsItemSentiment", - "TopicPageResponseRelatedTopicsItem", - "TopicPageResponseRelatedTopicsItemSentiment", - "TopicPageResponseStats", - "TopicPageResponseVoicesItem", - "TopicPageResponseVoicesItemRole", - "TopicPageResponseVoicesItemSentiment", "Tracker", "TrackerListResponse", "TrackerMutationResponse", - "TranscriptEditSubmittedResponse", - "TranscriptEditSubmittedResponseEdit", - "TranscriptEditSubmittedResponseEditStatus", "TranscriptJob", "TranscriptJobCharge", "TranscriptJobChargeFrom", @@ -1312,16 +736,6 @@ def __dir__(): "TranscriptJobStatus", "TranscriptPending", "TranscriptPendingQuality", - "TranscriptPreparationRequired", - "TranscriptPreparationRequiredAction", - "TranscriptPreparationRequiredActionBody", - "TranscriptPreparationRequiredActionMethod", - "TranscriptPreparationRequiredLastAttempt", - "TranscriptPreparationRequiredQuality", - "TranscriptPreparationRequiredQuote", - "TranscriptPreparationRequiredQuoteCharge", - "TranscriptPreparationRequiredQuoteChargeFrom", - "TranscriptPreparationRequiredQuoteChargeUnit", "TranscriptPurchaseQuote", "TranscriptPurchaseQuoteBillingScope", "TranscriptPurchaseQuoteCharge", @@ -1331,9 +745,13 @@ def __dir__(): "TranscriptQuote", "TranscriptRequestListResponse", "TranscriptRequestListResponseRequestsItem", - "TranscriptRequestSubmitResponse", "TranscriptResponse", "TranscriptResponseAccess", + "TranscriptResponseAccessDetails", + "TranscriptResponseAccessDetailsQuote", + "TranscriptResponseAccessDetailsQuoteCharge", + "TranscriptResponseAccessDetailsQuoteChargeFrom", + "TranscriptResponseAccessDetailsQuoteChargeUnit", "TranscriptResponseAccessGate", "TranscriptResponseAccessReason", "TranscriptResponseAccessType", @@ -1347,33 +765,31 @@ def __dir__(): "TranscriptResponseSpeakersItem", "TranscriptResult", "TranscriptResult_Pending", - "TranscriptResult_PreparationRequired", "TranscriptResult_Ready", "TranscriptSearchChunk", "TranscriptSearchResponse", "TranscriptSearchResponseAccess", + "TranscriptSearchResponseAccessDetails", + "TranscriptSearchResponseAccessDetailsQuote", + "TranscriptSearchResponseAccessDetailsQuoteCharge", + "TranscriptSearchResponseAccessDetailsQuoteChargeFrom", + "TranscriptSearchResponseAccessDetailsQuoteChargeUnit", "TranscriptSearchResponseAccessGate", "TranscriptSearchResponseAccessReason", "TranscriptSearchResponseAccessType", "TranscriptSearchResponseAccessUnlock", "TranscriptSearchResponseAccessUnlockAction", "TranscriptSearchResponseFilters", + "TranscriptSearchResponseFiltersKindItem", "TranscriptSearchResponseSearchIndex", "TranscriptSearchResponseSearchIndexState", + "TranscriptSearchResponseUnlock", "TranscriptSettings", "TranscriptSettingsQuality", "TranscriptVideo", - "VideoCaptionsResponse", - "VideoMergeListResponse", - "VideoMergeListResponseMergesItem", - "VideoMergeListResponseMergesItemStatus", - "VideoMergeSubmittedResponse", - "VideoMergeSubmittedResponseMerge", - "VideoMergeSubmittedResponseMergeStatus", "WebhookSecretRotateResponse", - "WithdrawnResponse", "WrongClassificationChange", - "WrongClassificationChangeMentionClass", + "WrongClassificationChangeClass", "WrongEntityChange", "WrongEntityTypeChange", "WrongEntityTypeChangeField", diff --git a/src/arcmira/types/alert.py b/src/arcmira/types/alert.py index ae50946..0509201 100644 --- a/src/arcmira/types/alert.py +++ b/src/arcmira/types/alert.py @@ -35,14 +35,9 @@ class Alert(UniversalBaseModel): Public id ("men_{n}") of the mention/appearance row that triggered the alert. Joins directly against mention rows (e.g. /v1/mentions). Null when not appearance-scoped. """ - media_id: typing.Optional[int] = pydantic.Field(default=None) + video_id: typing.Optional[str] = pydantic.Field(default=None) """ - Media row that triggered the alert. A raw integer database id, matching the numeric media ids used elsewhere in the API (e.g. mention media.id). Null when not media-scoped. - """ - - appearance_id: typing.Optional[int] = pydantic.Field(default=None) - """ - Appearance row that triggered the alert. A raw integer database id, matching the numeric appearance_id on mention rows (same number as in mention_id). Null when not appearance-scoped. + YouTube video id (11 characters) of the video that triggered the alert. Null when not media-scoped. """ excerpt_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/arcmira/types/alert_list_response.py b/src/arcmira/types/alert_list_response.py index f111fcb..d07eced 100644 --- a/src/arcmira/types/alert_list_response.py +++ b/src/arcmira/types/alert_list_response.py @@ -8,7 +8,7 @@ class AlertListResponse(UniversalBaseModel): - data: typing.List[Alert] = pydantic.Field() + alerts: typing.List[Alert] = pydantic.Field() """ Newest alerts first. """ diff --git a/src/arcmira/types/channel_guest_list_response.py b/src/arcmira/types/channel_guest_list_response.py deleted file mode 100644 index 145ce7c..0000000 --- a/src/arcmira/types/channel_guest_list_response.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_guest_list_response_export_capabilities import ChannelGuestListResponseExportCapabilities -from .channel_guest_list_response_items_item import ChannelGuestListResponseItemsItem -from .exposure_meta import ExposureMeta - - -class ChannelGuestListResponse(UniversalBaseModel): - items: typing.List[ChannelGuestListResponseItemsItem] = pydantic.Field() - """ - People who appeared on the channel, highest count first by default. count is the episodes they appeared in. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - export_capabilities: typing_extensions.Annotated[ - ChannelGuestListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_guest_list_response_export_capabilities.py b/src/arcmira/types/channel_guest_list_response_export_capabilities.py deleted file mode 100644 index 96819ed..0000000 --- a/src/arcmira/types/channel_guest_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ChannelGuestListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_guest_list_response_items_item.py b/src/arcmira/types/channel_guest_list_response_items_item.py deleted file mode 100644 index b59ac36..0000000 --- a/src/arcmira/types/channel_guest_list_response_items_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_guest_list_response_items_item_sentiment import ChannelGuestListResponseItemsItemSentiment - - -class ChannelGuestListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the person. - """ - - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ChannelGuestListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_guest_list_response_items_item_sentiment.py b/src/arcmira/types/channel_guest_list_response_items_item_sentiment.py deleted file mode 100644 index 7e59e41..0000000 --- a/src/arcmira/types/channel_guest_list_response_items_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelGuestListResponseItemsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/channel_page_response.py b/src/arcmira/types/channel_page_response.py deleted file mode 100644 index 0fb2382..0000000 --- a/src/arcmira/types/channel_page_response.py +++ /dev/null @@ -1,103 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_channel_info import ChannelPageResponseChannelInfo -from .channel_page_response_entity import ChannelPageResponseEntity -from .channel_page_response_episodes_by_month_item import ChannelPageResponseEpisodesByMonthItem -from .channel_page_response_episodes_item import ChannelPageResponseEpisodesItem -from .channel_page_response_guests_item import ChannelPageResponseGuestsItem -from .channel_page_response_hosts_detailed_item import ChannelPageResponseHostsDetailedItem -from .channel_page_response_organizations_item import ChannelPageResponseOrganizationsItem -from .channel_page_response_products_item import ChannelPageResponseProductsItem -from .channel_page_response_recommendations_summary import ChannelPageResponseRecommendationsSummary -from .channel_page_response_stats import ChannelPageResponseStats -from .channel_page_response_topics_item import ChannelPageResponseTopicsItem -from .exposure_meta import ExposureMeta - - -class ChannelPageResponse(UniversalBaseModel): - entity: ChannelPageResponseEntity = pydantic.Field() - """ - The channel and who owns it. - """ - - hosts: typing.List[str] = pydantic.Field() - """ - Host names: configured hosts when the channel has them, else discovered ones. - """ - - stats: ChannelPageResponseStats = pydantic.Field() - """ - Header vitals. Open on every plan. - """ - - channel_info: typing_extensions.Annotated[ - ChannelPageResponseChannelInfo, - FieldMetadata(alias="channelInfo"), - pydantic.Field(alias="channelInfo", description="Channel details, some gated by plan."), - ] - """ - Channel details, some gated by plan. - """ - - episodes_by_month: typing_extensions.Annotated[ - typing.List[ChannelPageResponseEpisodesByMonthItem], - FieldMetadata(alias="episodesByMonth"), - pydantic.Field( - alias="episodesByMonth", - description="Videos published per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Videos published per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - topics: typing.List[ChannelPageResponseTopicsItem] = pydantic.Field() - """ - Topics across the channel, highest count first. - """ - - hosts_detailed: typing.List[ChannelPageResponseHostsDetailedItem] = pydantic.Field() - """ - Hosts with their details, in the same order as hosts. - """ - - guests: typing.List[ChannelPageResponseGuestsItem] = pydantic.Field() - """ - Guests who are not hosts, most appearances first. - """ - - organizations: typing.List[ChannelPageResponseOrganizationsItem] = pydantic.Field() - """ - Organizations mentioned across the channel, most mentions first. - """ - - products: typing.List[ChannelPageResponseProductsItem] = pydantic.Field() - """ - Products mentioned across the channel, most mentions first. - """ - - episodes: typing.List[ChannelPageResponseEpisodesItem] = pydantic.Field() - """ - The channel's newest 50 indexed videos. Open on every plan. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - recommendations_summary: ChannelPageResponseRecommendationsSummary = pydantic.Field() - """ - Sponsor teaser for the channel. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_channel_info.py b/src/arcmira/types/channel_page_response_channel_info.py deleted file mode 100644 index fc523d9..0000000 --- a/src/arcmira/types/channel_page_response_channel_info.py +++ /dev/null @@ -1,72 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ChannelPageResponseChannelInfo(UniversalBaseModel): - """ - Channel details, some gated by plan. - """ - - subscriber_count: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="subscriberCount"), - pydantic.Field( - alias="subscriberCount", description="Subscriber count as YouTube displays it. Null when unknown." - ), - ] = None - """ - Subscriber count as YouTube displays it. Null when unknown. - """ - - avg_views: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="avgViews"), - pydantic.Field( - alias="avgViews", description="Average views per video as display text. Null when the plan hides it." - ), - ] = None - """ - Average views per video as display text. Null when the plan hides it. - """ - - episode_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="episodeCount"), - pydantic.Field(alias="episodeCount", description="Indexed videos. Null when the plan hides it."), - ] = None - """ - Indexed videos. Null when the plan hides it. - """ - - guest_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="guestCount"), - pydantic.Field(alias="guestCount", description="Unique guests. Null when the plan hides it."), - ] = None - """ - Unique guests. Null when the plan hides it. - """ - - start_date: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="startDate"), - pydantic.Field(alias="startDate", description="Publish date of the oldest indexed video. Null when none."), - ] = None - """ - Publish date of the oldest indexed video. Null when none. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_entity.py b/src/arcmira/types/channel_page_response_entity.py deleted file mode 100644 index 38b421f..0000000 --- a/src/arcmira/types/channel_page_response_entity.py +++ /dev/null @@ -1,122 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_entity_owner import ChannelPageResponseEntityOwner -from .channel_page_response_entity_type import ChannelPageResponseEntityType - - -class ChannelPageResponseEntity(UniversalBaseModel): - """ - The channel and who owns it. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Channel name. - """ - - display_name: str = pydantic.Field() - """ - Label that tells the channel apart from an owner of the same name, e.g. "AdQuick (channel)". Equals name otherwise. - """ - - type: ChannelPageResponseEntityType = pydantic.Field() - """ - Always channel. - """ - - category: str = pydantic.Field() - """ - The stored platform, or YouTube when none is stored. - """ - - platform: str = pydantic.Field() - """ - The stored platform, or youtube when none is stored. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id (UC form). Null when unknown."), - ] = None - """ - YouTube channel id (UC form). Null when unknown. - """ - - youtube_channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="youtubeChannelId"), - pydantic.Field(alias="youtubeChannelId", description="Same value as channelId."), - ] = None - """ - Same value as channelId. - """ - - youtube_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="youtubeHandle"), - pydantic.Field(alias="youtubeHandle", description="YouTube @handle. Null when unknown."), - ] = None - """ - YouTube @handle. Null when unknown. - """ - - url: typing.Optional[str] = pydantic.Field(default=None) - """ - Channel URL. Null when unknown. - """ - - avatar_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="avatarUrl"), - pydantic.Field(alias="avatarUrl", description="Channel avatar URL. Null when unknown."), - ] = None - """ - Channel avatar URL. Null when unknown. - """ - - avatar_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="avatarCheckedAt"), - pydantic.Field( - alias="avatarCheckedAt", - description="When the image pipeline last checked the stored avatar. Null when the avatar came from channel metadata instead.", - ), - ] = None - """ - When the image pipeline last checked the stored avatar. Null when the avatar came from channel metadata instead. - """ - - banner_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="bannerUrl"), - pydantic.Field(alias="bannerUrl", description="Channel banner URL. Null when unknown."), - ] = None - """ - Channel banner URL. Null when unknown. - """ - - owner: typing.Optional[ChannelPageResponseEntityOwner] = pydantic.Field(default=None) - """ - The organization or person that owns this entity. Null when no owner is recorded. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_entity_owner.py b/src/arcmira/types/channel_page_response_entity_owner.py deleted file mode 100644 index fc610f8..0000000 --- a/src/arcmira/types/channel_page_response_entity_owner.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ChannelPageResponseEntityOwner(UniversalBaseModel): - """ - The organization or person that owns this entity. Null when no owner is recorded. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id of the owner. - """ - - name: str = pydantic.Field() - """ - Owner name. - """ - - type: str = pydantic.Field() - """ - Owner entity type: organization (legacy rows may read company or brand) or person. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the owner page on arcmira.com. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_entity_type.py b/src/arcmira/types/channel_page_response_entity_type.py deleted file mode 100644 index 14f3ab8..0000000 --- a/src/arcmira/types/channel_page_response_entity_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseEntityType = typing.Union[typing.Literal["channel"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_episodes_by_month_item.py b/src/arcmira/types/channel_page_response_episodes_by_month_item.py deleted file mode 100644 index 7d38def..0000000 --- a/src/arcmira/types/channel_page_response_episodes_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ChannelPageResponseEpisodesByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_episodes_item.py b/src/arcmira/types/channel_page_response_episodes_item.py deleted file mode 100644 index d02107a..0000000 --- a/src/arcmira/types/channel_page_response_episodes_item.py +++ /dev/null @@ -1,130 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_episodes_item_platform import ChannelPageResponseEpisodesItemPlatform -from .channel_page_response_episodes_item_sentiment import ChannelPageResponseEpisodesItemSentiment -from .channel_page_response_episodes_item_timestamp import ChannelPageResponseEpisodesItemTimestamp -from .channel_page_response_episodes_item_type import ChannelPageResponseEpisodesItemType - - -class ChannelPageResponseEpisodesItem(UniversalBaseModel): - id: str = pydantic.Field() - """ - Raw media row id, as a string. - """ - - date: str = pydantic.Field() - """ - Publish date as locale display text, or Unknown. - """ - - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - channel: str = pydantic.Field() - """ - The channel name. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field( - alias="channelId", description="YouTube channel id of the video, else the channel's. Null when unknown." - ), - ] = None - """ - YouTube channel id of the video, else the channel's. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="The channel's YouTube handle. Null when unknown."), - ] = None - """ - The channel's YouTube handle. Null when unknown. - """ - - platform: ChannelPageResponseEpisodesItemPlatform = pydantic.Field() - """ - Always youtube. - """ - - thumbnail: str = pydantic.Field() - """ - Video thumbnail URL. - """ - - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL. Same value as thumbnail."), - ] - """ - Video thumbnail URL. Same value as thumbnail. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - duration: typing.Optional[float] = pydantic.Field(default=None) - """ - Video length in seconds. Null when unknown. - """ - - type: ChannelPageResponseEpisodesItemType = pydantic.Field() - """ - interview when a guest appeared, solo otherwise. - """ - - context: str = pydantic.Field() - """ - "Interview with {guest}" or "Episode". - """ - - sentiment: ChannelPageResponseEpisodesItemSentiment = pydantic.Field() - """ - Always neutral. - """ - - timestamp: ChannelPageResponseEpisodesItemTimestamp = pydantic.Field() - """ - Always 00:00. - """ - - guest: typing.Optional[str] = pydantic.Field(default=None) - """ - One guest who appeared in the video. Null when none. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_episodes_item_platform.py b/src/arcmira/types/channel_page_response_episodes_item_platform.py deleted file mode 100644 index ca3d6d3..0000000 --- a/src/arcmira/types/channel_page_response_episodes_item_platform.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseEpisodesItemPlatform = typing.Union[typing.Literal["youtube"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_episodes_item_sentiment.py b/src/arcmira/types/channel_page_response_episodes_item_sentiment.py deleted file mode 100644 index 27a850f..0000000 --- a/src/arcmira/types/channel_page_response_episodes_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseEpisodesItemSentiment = typing.Union[typing.Literal["neutral"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_episodes_item_timestamp.py b/src/arcmira/types/channel_page_response_episodes_item_timestamp.py deleted file mode 100644 index 978a840..0000000 --- a/src/arcmira/types/channel_page_response_episodes_item_timestamp.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseEpisodesItemTimestamp = typing.Union[typing.Literal["00:00"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_episodes_item_type.py b/src/arcmira/types/channel_page_response_episodes_item_type.py deleted file mode 100644 index 2af698c..0000000 --- a/src/arcmira/types/channel_page_response_episodes_item_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseEpisodesItemType = typing.Union[typing.Literal["interview", "solo"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_guests_item.py b/src/arcmira/types/channel_page_response_guests_item.py deleted file mode 100644 index 1e504b6..0000000 --- a/src/arcmira/types/channel_page_response_guests_item.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_guests_item_role import ChannelPageResponseGuestsItemRole -from .channel_page_response_guests_item_sentiment import ChannelPageResponseGuestsItemSentiment - - -class ChannelPageResponseGuestsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Videos on the channel the guest appeared in. Null when the plan hides counts. - """ - - sentiment: ChannelPageResponseGuestsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - role: ChannelPageResponseGuestsItemRole = pydantic.Field() - """ - Always Guest. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_guests_item_role.py b/src/arcmira/types/channel_page_response_guests_item_role.py deleted file mode 100644 index 45d3b44..0000000 --- a/src/arcmira/types/channel_page_response_guests_item_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseGuestsItemRole = typing.Union[typing.Literal["Guest"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_guests_item_sentiment.py b/src/arcmira/types/channel_page_response_guests_item_sentiment.py deleted file mode 100644 index 71bb7e7..0000000 --- a/src/arcmira/types/channel_page_response_guests_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseGuestsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_hosts_detailed_item.py b/src/arcmira/types/channel_page_response_hosts_detailed_item.py deleted file mode 100644 index 2f398eb..0000000 --- a/src/arcmira/types/channel_page_response_hosts_detailed_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_hosts_detailed_item_sentiment import ChannelPageResponseHostsDetailedItemSentiment - - -class ChannelPageResponseHostsDetailedItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Host name. - """ - - role: str = pydantic.Field() - """ - The recorded role cue, or Host. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Appearances by the host. Null when the plan hides counts. - """ - - sentiment: ChannelPageResponseHostsDetailedItemSentiment = pydantic.Field() - """ - Always positive. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Host image URL. Null until resolved."), - ] = None - """ - Host image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this person. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this person. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_hosts_detailed_item_sentiment.py b/src/arcmira/types/channel_page_response_hosts_detailed_item_sentiment.py deleted file mode 100644 index d113254..0000000 --- a/src/arcmira/types/channel_page_response_hosts_detailed_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseHostsDetailedItemSentiment = typing.Union[typing.Literal["positive"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_organizations_item.py b/src/arcmira/types/channel_page_response_organizations_item.py deleted file mode 100644 index d1ef77f..0000000 --- a/src/arcmira/types/channel_page_response_organizations_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_organizations_item_sentiment import ChannelPageResponseOrganizationsItemSentiment - - -class ChannelPageResponseOrganizationsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ChannelPageResponseOrganizationsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_organizations_item_sentiment.py b/src/arcmira/types/channel_page_response_organizations_item_sentiment.py deleted file mode 100644 index 1e5879f..0000000 --- a/src/arcmira/types/channel_page_response_organizations_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseOrganizationsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/channel_page_response_products_item.py b/src/arcmira/types/channel_page_response_products_item.py deleted file mode 100644 index 3a69347..0000000 --- a/src/arcmira/types/channel_page_response_products_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_page_response_products_item_sentiment import ChannelPageResponseProductsItemSentiment - - -class ChannelPageResponseProductsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ChannelPageResponseProductsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_products_item_sentiment.py b/src/arcmira/types/channel_page_response_products_item_sentiment.py deleted file mode 100644 index eff13a6..0000000 --- a/src/arcmira/types/channel_page_response_products_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseProductsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_topics_item.py b/src/arcmira/types/channel_page_response_topics_item.py deleted file mode 100644 index 3493421..0000000 --- a/src/arcmira/types/channel_page_response_topics_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .channel_page_response_topics_item_sentiment import ChannelPageResponseTopicsItemSentiment - - -class ChannelPageResponseTopicsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Videos on the channel with the topic. Null when the plan hides counts. - """ - - sentiment: ChannelPageResponseTopicsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_topics_item_sentiment.py b/src/arcmira/types/channel_page_response_topics_item_sentiment.py deleted file mode 100644 index a2dc862..0000000 --- a/src/arcmira/types/channel_page_response_topics_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelPageResponseTopicsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/channel_sponsor.py b/src/arcmira/types/channel_sponsor.py index f98cf8f..99af5fa 100644 --- a/src/arcmira/types/channel_sponsor.py +++ b/src/arcmira/types/channel_sponsor.py @@ -4,16 +4,12 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .channel_sponsor_entity import ChannelSponsorEntity from .channel_sponsor_sponsor_status import ChannelSponsorSponsorStatus +from .entity_ref import EntityRef class ChannelSponsor(UniversalBaseModel): - entity: ChannelSponsorEntity = pydantic.Field() - """ - The sponsoring entity. - """ - + entity: EntityRef ad_reads: int = pydantic.Field() """ Number of ad_read recommendation rows for this sponsor on the channel. diff --git a/src/arcmira/types/channel_sponsor_entity.py b/src/arcmira/types/channel_sponsor_entity.py deleted file mode 100644 index c50c4dd..0000000 --- a/src/arcmira/types/channel_sponsor_entity.py +++ /dev/null @@ -1,46 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ChannelSponsorEntity(UniversalBaseModel): - """ - The sponsoring entity. - """ - - id: str = pydantic.Field() - """ - Public entity id in the form "ent_{n}". - """ - - name: str = pydantic.Field() - """ - Canonical entity name. - """ - - type: str = pydantic.Field() - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - URL slug, the site's canonical id for every type but channel. Null when never slugged. - """ - - page: typing.Optional[str] = pydantic.Field(default=None) - """ - The entity's page on arcmira.com, absolute. Link the name to it when you write the entity into an answer. Null for a type the site has no page for. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access.py b/src/arcmira/types/channel_sponsors_response_access.py index cd6ea7e..4f2ca07 100644 --- a/src/arcmira/types/channel_sponsors_response_access.py +++ b/src/arcmira/types/channel_sponsors_response_access.py @@ -4,6 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .channel_sponsors_response_access_details import ChannelSponsorsResponseAccessDetails from .channel_sponsors_response_access_gate import ChannelSponsorsResponseAccessGate from .channel_sponsors_response_access_reason import ChannelSponsorsResponseAccessReason from .channel_sponsors_response_access_type import ChannelSponsorsResponseAccessType @@ -67,6 +68,11 @@ class ChannelSponsorsResponseAccess(UniversalBaseModel): On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. """ + details: typing.Optional[ChannelSponsorsResponseAccessDetails] = pydantic.Field(default=None) + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + doc_url: str request_id: str diff --git a/src/arcmira/types/channel_sponsors_response_access_details.py b/src/arcmira/types/channel_sponsors_response_access_details.py new file mode 100644 index 0000000..bf943ad --- /dev/null +++ b/src/arcmira/types/channel_sponsors_response_access_details.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .channel_sponsors_response_access_details_quote import ChannelSponsorsResponseAccessDetailsQuote + + +class ChannelSponsorsResponseAccessDetails(UniversalBaseModel): + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + + quote: typing.Optional[ChannelSponsorsResponseAccessDetailsQuote] = pydantic.Field(default=None) + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + existing_request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. + """ + + existing_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote.py b/src/arcmira/types/channel_sponsors_response_access_details_quote.py new file mode 100644 index 0000000..269911f --- /dev/null +++ b/src/arcmira/types/channel_sponsors_response_access_details_quote.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .channel_sponsors_response_access_details_quote_charge import ChannelSponsorsResponseAccessDetailsQuoteCharge +from .transcript_quote import TranscriptQuote + + +class ChannelSponsorsResponseAccessDetailsQuote(TranscriptQuote): + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + charge: typing.Optional[ChannelSponsorsResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) + """ + The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py new file mode 100644 index 0000000..0a2c777 --- /dev/null +++ b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .channel_sponsors_response_access_details_quote_charge_from import ( + ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, +) +from .channel_sponsors_response_access_details_quote_charge_unit import ( + ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, +) + + +class ChannelSponsorsResponseAccessDetailsQuoteCharge(UniversalBaseModel): + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + unit: ChannelSponsorsResponseAccessDetailsQuoteChargeUnit + amount: float + from_: typing_extensions.Annotated[ + ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, + FieldMetadata(alias="from"), + pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), + ] + """ + Where the charge would come from at the current balance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py new file mode 100644 index 0000000..13178a4 --- /dev/null +++ b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ChannelSponsorsResponseAccessDetailsQuoteChargeFrom = typing.Union[ + typing.Literal["included", "on_demand", "mixed"], typing.Any +] diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py new file mode 100644 index 0000000..5d6ff6f --- /dev/null +++ b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ChannelSponsorsResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/channel_videos_response.py b/src/arcmira/types/channel_videos_response.py index 7bb8580..658da83 100644 --- a/src/arcmira/types/channel_videos_response.py +++ b/src/arcmira/types/channel_videos_response.py @@ -6,6 +6,7 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .channel_videos_response_channel import ChannelVideosResponseChannel from .channel_videos_response_episodes_item import ChannelVideosResponseEpisodesItem +from .publication_window import PublicationWindow class ChannelVideosResponse(UniversalBaseModel): @@ -26,6 +27,7 @@ class ChannelVideosResponse(UniversalBaseModel): Signed continuation for the next page. Null on the last page. """ + window: PublicationWindow indexed_through: typing.Optional[str] = pydantic.Field(default=None) """ Newest publish date among every indexed video of the channel, whatever window was asked for. Null when nothing is indexed. diff --git a/src/arcmira/types/correction_accepted_response.py b/src/arcmira/types/correction_accepted_response.py deleted file mode 100644 index 7d6eb1c..0000000 --- a/src/arcmira/types/correction_accepted_response.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .correction_accepted_response_kind import CorrectionAcceptedResponseKind - - -class CorrectionAcceptedResponse(UniversalBaseModel): - ok: bool = pydantic.Field() - """ - Always true on acceptance. - """ - - kind: CorrectionAcceptedResponseKind = pydantic.Field() - """ - The correction kind, echoed back. - """ - - result: typing.Dict[str, typing.Any] = pydantic.Field() - """ - The stored pending-review row (kind-specific fields). Always carries the row id: use it with the matching withdrawal DELETE. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/correction_accepted_response_kind.py b/src/arcmira/types/correction_accepted_response_kind.py deleted file mode 100644 index 2527b13..0000000 --- a/src/arcmira/types/correction_accepted_response_kind.py +++ /dev/null @@ -1,8 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -CorrectionAcceptedResponseKind = typing.Union[ - typing.Literal["line_edit", "speaker_reassign", "speaker_identify", "add_person", "entity_tag", "segment_rewrite"], - typing.Any, -] diff --git a/src/arcmira/types/entity.py b/src/arcmira/types/entity.py index 73a952e..f6f0afb 100644 --- a/src/arcmira/types/entity.py +++ b/src/arcmira/types/entity.py @@ -12,11 +12,6 @@ class Entity(UniversalBaseModel): Public entity id in the form "ent_{n}". Always the canonical entity id. """ - numeric_id: int = pydantic.Field() - """ - Raw integer database id of the canonical entity. Prefer the public "ent_{n}" id in requests. - """ - canonical_id: str = pydantic.Field() """ Public id of the canonical entity. Identical to id. diff --git a/src/arcmira/types/entity_card.py b/src/arcmira/types/entity_card.py deleted file mode 100644 index c2bf183..0000000 --- a/src/arcmira/types/entity_card.py +++ /dev/null @@ -1,77 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class EntityCard(UniversalBaseModel): - id: int = pydantic.Field() - """ - The REQUESTED raw integer entity id (even when it was merged; the card carries the canonical entity's data under the requested id). - """ - - name: str = pydantic.Field() - """ - Canonical entity name. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - URL slug. Null when the entity has never been slugged. - """ - - type: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - image_url: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity image URL. Null until an image has been resolved. - """ - - subtitle: typing.Optional[typing.Any] = pydantic.Field(default=None) - """ - Reserved for a future category/role line. Always null today. - """ - - index_mentions: int = pydantic.Field() - """ - Total indexed mention rows for the entity. 0 when none. - """ - - index_appearances: typing.Optional[int] = pydantic.Field(default=None) - """ - Indexed physical-appearance rows. Only populated for person entities; null for every other type. - """ - - indexed_videos: typing.Optional[int] = pydantic.Field(default=None) - """ - Completed videos published by this channel and indexed by Arcmira. Null for other entity types. - """ - - average_views: typing.Optional[float] = pydantic.Field(default=None) - """ - Mean of known positive view counts for the channel's indexed videos. Null when unavailable or for other types. - """ - - youtube_channel_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Canonical YouTube channel identifier, when known. - """ - - youtube_handle: typing.Optional[str] = pydantic.Field(default=None) - """ - YouTube channel handle, when known. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_cards_response.py b/src/arcmira/types/entity_cards_response.py deleted file mode 100644 index 8537a59..0000000 --- a/src/arcmira/types/entity_cards_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_card import EntityCard - - -class EntityCardsResponse(UniversalBaseModel): - cards: typing.List[EntityCard] = pydantic.Field() - """ - One card per requested id that resolved to an entity, in requested order. Unknown ids are silently dropped. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_channel_list_response.py b/src/arcmira/types/entity_channel_list_response.py deleted file mode 100644 index 39b6ada..0000000 --- a/src/arcmira/types/entity_channel_list_response.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_channel_list_response_export_capabilities import EntityChannelListResponseExportCapabilities -from .entity_channel_list_response_items_item import EntityChannelListResponseItemsItem -from .exposure_meta import ExposureMeta - - -class EntityChannelListResponse(UniversalBaseModel): - items: typing.List[EntityChannelListResponseItemsItem] = pydantic.Field() - """ - Channels whose media carry the entity, highest count first by default. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - export_capabilities: typing_extensions.Annotated[ - EntityChannelListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_channel_list_response_export_capabilities.py b/src/arcmira/types/entity_channel_list_response_export_capabilities.py deleted file mode 100644 index 2394a87..0000000 --- a/src/arcmira/types/entity_channel_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityChannelListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_channel_list_response_items_item.py b/src/arcmira/types/entity_channel_list_response_items_item.py deleted file mode 100644 index d873140..0000000 --- a/src/arcmira/types/entity_channel_list_response_items_item.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityChannelListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the channel. - """ - - name: str = pydantic.Field() - """ - Channel name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media on this channel counted for the entity: appearances for a person, mentions otherwise. Null when the plan hides counts. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Channel avatar URL. Null until an image has been resolved."), - ] = None - """ - Channel avatar URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_lookup_response.py b/src/arcmira/types/entity_lookup_response.py deleted file mode 100644 index 9c7bab2..0000000 --- a/src/arcmira/types/entity_lookup_response.py +++ /dev/null @@ -1,20 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity import Entity - - -class EntityLookupResponse(UniversalBaseModel): - entity: Entity - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access.py b/src/arcmira/types/entity_momentum_response_access.py index f3749f3..11b0237 100644 --- a/src/arcmira/types/entity_momentum_response_access.py +++ b/src/arcmira/types/entity_momentum_response_access.py @@ -4,6 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .entity_momentum_response_access_details import EntityMomentumResponseAccessDetails from .entity_momentum_response_access_gate import EntityMomentumResponseAccessGate from .entity_momentum_response_access_reason import EntityMomentumResponseAccessReason from .entity_momentum_response_access_type import EntityMomentumResponseAccessType @@ -67,6 +68,11 @@ class EntityMomentumResponseAccess(UniversalBaseModel): On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. """ + details: typing.Optional[EntityMomentumResponseAccessDetails] = pydantic.Field(default=None) + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + doc_url: str request_id: str diff --git a/src/arcmira/types/entity_momentum_response_access_details.py b/src/arcmira/types/entity_momentum_response_access_details.py new file mode 100644 index 0000000..e9e4eed --- /dev/null +++ b/src/arcmira/types/entity_momentum_response_access_details.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .entity_momentum_response_access_details_quote import EntityMomentumResponseAccessDetailsQuote + + +class EntityMomentumResponseAccessDetails(UniversalBaseModel): + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + + quote: typing.Optional[EntityMomentumResponseAccessDetailsQuote] = pydantic.Field(default=None) + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + existing_request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. + """ + + existing_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote.py b/src/arcmira/types/entity_momentum_response_access_details_quote.py new file mode 100644 index 0000000..5c08c64 --- /dev/null +++ b/src/arcmira/types/entity_momentum_response_access_details_quote.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .entity_momentum_response_access_details_quote_charge import EntityMomentumResponseAccessDetailsQuoteCharge +from .transcript_quote import TranscriptQuote + + +class EntityMomentumResponseAccessDetailsQuote(TranscriptQuote): + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + charge: typing.Optional[EntityMomentumResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) + """ + The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py new file mode 100644 index 0000000..e416a78 --- /dev/null +++ b/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .entity_momentum_response_access_details_quote_charge_from import ( + EntityMomentumResponseAccessDetailsQuoteChargeFrom, +) +from .entity_momentum_response_access_details_quote_charge_unit import ( + EntityMomentumResponseAccessDetailsQuoteChargeUnit, +) + + +class EntityMomentumResponseAccessDetailsQuoteCharge(UniversalBaseModel): + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + unit: EntityMomentumResponseAccessDetailsQuoteChargeUnit + amount: float + from_: typing_extensions.Annotated[ + EntityMomentumResponseAccessDetailsQuoteChargeFrom, + FieldMetadata(alias="from"), + pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), + ] + """ + Where the charge would come from at the current balance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py new file mode 100644 index 0000000..6543f79 --- /dev/null +++ b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EntityMomentumResponseAccessDetailsQuoteChargeFrom = typing.Union[ + typing.Literal["included", "on_demand", "mixed"], typing.Any +] diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py new file mode 100644 index 0000000..709bb2e --- /dev/null +++ b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +EntityMomentumResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/entity_organization_list_response.py b/src/arcmira/types/entity_organization_list_response.py deleted file mode 100644 index 5a2651e..0000000 --- a/src/arcmira/types/entity_organization_list_response.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_organization_list_response_export_capabilities import EntityOrganizationListResponseExportCapabilities -from .entity_organization_list_response_items_item import EntityOrganizationListResponseItemsItem -from .exposure_meta import ExposureMeta - - -class EntityOrganizationListResponse(UniversalBaseModel): - items: typing.List[EntityOrganizationListResponseItemsItem] = pydantic.Field() - """ - Organizations that co-occur with the entity in indexed media, highest count first by default. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - export_capabilities: typing_extensions.Annotated[ - EntityOrganizationListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_organization_list_response_export_capabilities.py b/src/arcmira/types/entity_organization_list_response_export_capabilities.py deleted file mode 100644 index b271e51..0000000 --- a/src/arcmira/types/entity_organization_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityOrganizationListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_organization_list_response_items_item.py b/src/arcmira/types/entity_organization_list_response_items_item.py deleted file mode 100644 index 83540e6..0000000 --- a/src/arcmira/types/entity_organization_list_response_items_item.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_organization_list_response_items_item_sentiment import EntityOrganizationListResponseItemsItemSentiment - - -class EntityOrganizationListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the organization. - """ - - name: str = pydantic.Field() - """ - Organization name. - """ - - type: str = pydantic.Field() - """ - Stored organization type: organization, company, or brand. Rows served from the stored top-N read organization. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: EntityOrganizationListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Organization logo URL. Null until an image has been resolved."), - ] = None - """ - Organization logo URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_organization_list_response_items_item_sentiment.py b/src/arcmira/types/entity_organization_list_response_items_item_sentiment.py deleted file mode 100644 index a72b63e..0000000 --- a/src/arcmira/types/entity_organization_list_response_items_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityOrganizationListResponseItemsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/entity_page_mention.py b/src/arcmira/types/entity_page_mention.py deleted file mode 100644 index 2c017dd..0000000 --- a/src/arcmira/types/entity_page_mention.py +++ /dev/null @@ -1,158 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_page_mention_excerpt import EntityPageMentionExcerpt -from .entity_page_mention_platform import EntityPageMentionPlatform -from .entity_page_mention_sentiment import EntityPageMentionSentiment -from .entity_page_mention_type import EntityPageMentionType - - -class EntityPageMention(UniversalBaseModel): - id: str = pydantic.Field() - """ - Raw appearance row id (the first row for the video), as a string. - """ - - date: str = pydantic.Field() - """ - Publish date as locale display text, or Unknown. - """ - - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - channel: str = pydantic.Field() - """ - Source channel name, or Unknown Channel. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id of the source channel. Null when unknown."), - ] = None - """ - YouTube channel id of the source channel. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="YouTube handle of the source channel. Null when unknown."), - ] = None - """ - YouTube handle of the source channel. Null when unknown. - """ - - platform: EntityPageMentionPlatform = pydantic.Field() - """ - Always youtube. - """ - - thumbnail: str = pydantic.Field() - """ - Video thumbnail URL. - """ - - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL. Same value as thumbnail."), - ] - """ - Video thumbnail URL. Same value as thumbnail. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - duration: typing.Optional[float] = pydantic.Field(default=None) - """ - Video length in seconds. Null when unknown. - """ - - type: EntityPageMentionType = pydantic.Field() - """ - Always mention. - """ - - context: str = pydantic.Field() - """ - Description of the mention, or "No description available". - """ - - sentiment: typing.Optional[EntityPageMentionSentiment] = pydantic.Field(default=None) - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). Absent when no mention in the video carries a score. - """ - - timestamp: typing.Optional[str] = pydantic.Field(default=None) - """ - Earliest mention timestamp as MM:SS text. Null when the mention covers the full episode. - """ - - raw_timestamp: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="rawTimestamp"), - pydantic.Field(alias="rawTimestamp", description="Earliest mention timestamp as stored. Null when none."), - ] = None - """ - Earliest mention timestamp as stored. Null when none. - """ - - mention_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="mentionCount"), - pydantic.Field(alias="mentionCount", description="Mentions of the entity in this video. At least 1."), - ] - """ - Mentions of the entity in this video. At least 1. - """ - - all_timestamps: typing_extensions.Annotated[ - typing.Optional[typing.List[str]], - FieldMetadata(alias="allTimestamps"), - pydantic.Field( - alias="allTimestamps", - description="Every non-zero mention timestamp in the video, as MM:SS text. Absent when there are none.", - ), - ] = None - """ - Every non-zero mention timestamp in the video, as MM:SS text. Absent when there are none. - """ - - excerpt: typing.Optional[EntityPageMentionExcerpt] = pydantic.Field(default=None) - """ - A speakerless excerpt naming the entity in this media, when one is active. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_page_mention_excerpt.py b/src/arcmira/types/entity_page_mention_excerpt.py deleted file mode 100644 index 664c52d..0000000 --- a/src/arcmira/types/entity_page_mention_excerpt.py +++ /dev/null @@ -1,106 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_page_mention_excerpt_public_source_class import EntityPageMentionExcerptPublicSourceClass - - -class EntityPageMentionExcerpt(UniversalBaseModel): - """ - A speakerless excerpt naming the entity in this media, when one is active. - """ - - id: str = pydantic.Field() - """ - Published excerpt id. - """ - - exact_text: typing_extensions.Annotated[ - str, - FieldMetadata(alias="exactText"), - pydantic.Field(alias="exactText", description="The transcript span that names the entity."), - ] - """ - The transcript span that names the entity. - """ - - context_before: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="contextBefore"), - pydantic.Field(alias="contextBefore", description="Transcript text immediately before the span."), - ] = None - """ - Transcript text immediately before the span. - """ - - context_after: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="contextAfter"), - pydantic.Field(alias="contextAfter", description="Transcript text immediately after the span."), - ] = None - """ - Transcript text immediately after the span. - """ - - term: str = pydantic.Field() - """ - The surface form that matched in the transcript, which may be an approved alias. - """ - - term_char_start: typing_extensions.Annotated[ - int, - FieldMetadata(alias="termCharStart"), - pydantic.Field(alias="termCharStart", description="Character offset where term starts."), - ] - """ - Character offset where term starts. - """ - - term_char_end: typing_extensions.Annotated[ - int, - FieldMetadata(alias="termCharEnd"), - pydantic.Field(alias="termCharEnd", description="Character offset where term ends."), - ] - """ - Character offset where term ends. - """ - - start_seconds: typing_extensions.Annotated[ - float, - FieldMetadata(alias="startSeconds"), - pydantic.Field(alias="startSeconds", description="Span start in the video, in seconds."), - ] - """ - Span start in the video, in seconds. - """ - - end_seconds: typing_extensions.Annotated[ - typing.Optional[float], - FieldMetadata(alias="endSeconds"), - pydantic.Field(alias="endSeconds", description="Span end in the video, in seconds."), - ] = None - """ - Span end in the video, in seconds. - """ - - public_source_class: typing_extensions.Annotated[ - EntityPageMentionExcerptPublicSourceClass, - FieldMetadata(alias="publicSourceClass"), - pydantic.Field(alias="publicSourceClass", description="Transcript source class of the excerpt."), - ] - """ - Transcript source class of the excerpt. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_page_mention_excerpt_public_source_class.py b/src/arcmira/types/entity_page_mention_excerpt_public_source_class.py deleted file mode 100644 index 15ab32b..0000000 --- a/src/arcmira/types/entity_page_mention_excerpt_public_source_class.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPageMentionExcerptPublicSourceClass = typing.Union[ - typing.Literal["arcmira_premium_excerpt", "creator_captions", "third_party_quick"], typing.Any -] diff --git a/src/arcmira/types/entity_page_mention_platform.py b/src/arcmira/types/entity_page_mention_platform.py deleted file mode 100644 index 38a722f..0000000 --- a/src/arcmira/types/entity_page_mention_platform.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPageMentionPlatform = typing.Union[typing.Literal["youtube"], typing.Any] diff --git a/src/arcmira/types/entity_page_mention_sentiment.py b/src/arcmira/types/entity_page_mention_sentiment.py deleted file mode 100644 index 57c0782..0000000 --- a/src/arcmira/types/entity_page_mention_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPageMentionSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/entity_page_mention_type.py b/src/arcmira/types/entity_page_mention_type.py deleted file mode 100644 index 08feb1a..0000000 --- a/src/arcmira/types/entity_page_mention_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPageMentionType = typing.Union[typing.Literal["mention"], typing.Any] diff --git a/src/arcmira/types/entity_people_list_response.py b/src/arcmira/types/entity_people_list_response.py deleted file mode 100644 index 3ed45e0..0000000 --- a/src/arcmira/types/entity_people_list_response.py +++ /dev/null @@ -1,74 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_people_list_response_export_capabilities import EntityPeopleListResponseExportCapabilities -from .entity_people_list_response_items_item import EntityPeopleListResponseItemsItem -from .entity_people_list_response_people_mode import EntityPeopleListResponsePeopleMode -from .exposure_meta import ExposureMeta - - -class EntityPeopleListResponse(UniversalBaseModel): - items: typing.List[EntityPeopleListResponseItemsItem] = pydantic.Field() - """ - People that co-occur with the entity in indexed media, highest count first by default. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - people_mode: typing_extensions.Annotated[ - EntityPeopleListResponsePeopleMode, - FieldMetadata(alias="peopleMode"), - pydantic.Field( - alias="peopleMode", - description="Which co-occurrence the rows count. appearances: guest episodes. mentions: any shared media. Defaults to mentions for organizations and products, appearances otherwise.", - ), - ] - """ - Which co-occurrence the rows count. appearances: guest episodes. mentions: any shared media. Defaults to mentions for organizations and products, appearances otherwise. - """ - - export_capabilities: typing_extensions.Annotated[ - EntityPeopleListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_people_list_response_export_capabilities.py b/src/arcmira/types/entity_people_list_response_export_capabilities.py deleted file mode 100644 index 5b7c63f..0000000 --- a/src/arcmira/types/entity_people_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityPeopleListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_people_list_response_items_item.py b/src/arcmira/types/entity_people_list_response_items_item.py deleted file mode 100644 index 3d2f90d..0000000 --- a/src/arcmira/types/entity_people_list_response_items_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_people_list_response_items_item_sentiment import EntityPeopleListResponseItemsItemSentiment - - -class EntityPeopleListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the person. - """ - - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: EntityPeopleListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_people_list_response_items_item_sentiment.py b/src/arcmira/types/entity_people_list_response_items_item_sentiment.py deleted file mode 100644 index 06b9e2a..0000000 --- a/src/arcmira/types/entity_people_list_response_items_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPeopleListResponseItemsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/entity_people_list_response_people_mode.py b/src/arcmira/types/entity_people_list_response_people_mode.py deleted file mode 100644 index de0269d..0000000 --- a/src/arcmira/types/entity_people_list_response_people_mode.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityPeopleListResponsePeopleMode = typing.Union[typing.Literal["appearances", "mentions"], typing.Any] diff --git a/src/arcmira/types/entity_product_list_response.py b/src/arcmira/types/entity_product_list_response.py deleted file mode 100644 index d8b4676..0000000 --- a/src/arcmira/types/entity_product_list_response.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_product_list_response_export_capabilities import EntityProductListResponseExportCapabilities -from .entity_product_list_response_items_item import EntityProductListResponseItemsItem -from .exposure_meta import ExposureMeta - - -class EntityProductListResponse(UniversalBaseModel): - items: typing.List[EntityProductListResponseItemsItem] = pydantic.Field() - """ - Products related to the entity, highest count first by default. For an organization these are the products it owns that share media with it; for other types, products that co-occur in indexed media. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - export_capabilities: typing_extensions.Annotated[ - EntityProductListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_product_list_response_export_capabilities.py b/src/arcmira/types/entity_product_list_response_export_capabilities.py deleted file mode 100644 index 4b616f8..0000000 --- a/src/arcmira/types/entity_product_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityProductListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_product_list_response_items_item.py b/src/arcmira/types/entity_product_list_response_items_item.py deleted file mode 100644 index 3ad2e28..0000000 --- a/src/arcmira/types/entity_product_list_response_items_item.py +++ /dev/null @@ -1,77 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_product_list_response_items_item_sentiment import EntityProductListResponseItemsItemSentiment - - -class EntityProductListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the product. - """ - - name: str = pydantic.Field() - """ - Product name. - """ - - type: str = pydantic.Field() - """ - Stored entity type. Always product. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - avg_sentiment: typing_extensions.Annotated[ - typing.Optional[float], - FieldMetadata(alias="avgSentiment"), - pydantic.Field( - alias="avgSentiment", - description="Raw average sentiment score between -1 and 1. Null when no score was computed.", - ), - ] = None - """ - Raw average sentiment score between -1 and 1. Null when no score was computed. - """ - - sentiment: EntityProductListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Product logo URL. Null until an image has been resolved."), - ] = None - """ - Product logo URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_product_list_response_items_item_sentiment.py b/src/arcmira/types/entity_product_list_response_items_item_sentiment.py deleted file mode 100644 index 26d0b64..0000000 --- a/src/arcmira/types/entity_product_list_response_items_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityProductListResponseItemsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/entity_resolve_response.py b/src/arcmira/types/entity_resolve_response.py index ecfe44f..a9a352f 100644 --- a/src/arcmira/types/entity_resolve_response.py +++ b/src/arcmira/types/entity_resolve_response.py @@ -27,7 +27,7 @@ class EntityResolveResponse(UniversalBaseModel): """ best: typing.Optional[ResolveCandidate] = None - suggested: ResolveSuggestion + suggested: typing.Optional[ResolveSuggestion] = None ask: typing.Optional[EntityResolveResponseAsk] = pydantic.Field(default=None) """ Set when best and suggested are both null and several rows fit: show the options to the user, or check every option id against the data and answer per row. diff --git a/src/arcmira/types/entity_search_response.py b/src/arcmira/types/entity_search_response.py deleted file mode 100644 index 1d68c65..0000000 --- a/src/arcmira/types/entity_search_response.py +++ /dev/null @@ -1,29 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_search_result import EntitySearchResult - - -class EntitySearchResponse(UniversalBaseModel): - data: typing.List[EntitySearchResult] - query: str = pydantic.Field() - """ - The q parameter echoed back. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows matched than limit. There is no cursor; narrow q or pass type. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_search_result.py b/src/arcmira/types/entity_search_result.py deleted file mode 100644 index 6353b9f..0000000 --- a/src/arcmira/types/entity_search_result.py +++ /dev/null @@ -1,73 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_search_result_recommendations_summary import EntitySearchResultRecommendationsSummary - - -class EntitySearchResult(UniversalBaseModel): - id: str = pydantic.Field() - """ - Public entity id in the form "ent_{n}". - """ - - numeric_id: int = pydantic.Field() - """ - Raw integer database id. - """ - - name: str = pydantic.Field() - """ - Entity name. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - URL slug. Null when the entity has never been slugged. - """ - - type: str = pydantic.Field() - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - appearance_count: typing.Optional[int] = pydantic.Field(default=None) - """ - Number of indexed appearance/mention rows. Results are ordered by this, descending. - """ - - youtube_channel_id: typing.Optional[str] = pydantic.Field(default=None) - """ - YouTube channel id for channel entities. Null for every other type. - """ - - description: typing.Optional[str] = pydantic.Field(default=None) - """ - One catalog sentence that tells rows with the same name apart, for example "Common gender-neutral given name or nickname". Null when the catalog has none. - """ - - page: typing.Optional[str] = pydantic.Field(default=None) - """ - The entity's page on arcmira.com, absolute. Link the name to it when you write the entity into an answer. Null for a type the site has no page for. - """ - - suggested: bool = pydantic.Field() - """ - True on an exact match with far more traction than any other row of its type, or on the one row a UC id, @handle, or YouTube URL resolves to. Use it without asking. - """ - - recommendations_summary: typing.Optional[EntitySearchResultRecommendationsSummary] = pydantic.Field(default=None) - """ - Only present when the caller has Recommendations API access (a Pro+ plan) and a brand profile exists for the entity. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_search_result_recommendations_summary.py b/src/arcmira/types/entity_search_result_recommendations_summary.py deleted file mode 100644 index 29856e0..0000000 --- a/src/arcmira/types/entity_search_result_recommendations_summary.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class EntitySearchResultRecommendationsSummary(UniversalBaseModel): - """ - Only present when the caller has Recommendations API access (a Pro+ plan) and a brand profile exists for the entity. - """ - - total_ad_reads: int = pydantic.Field() - """ - Total ad_read rows for this entity. 0 when none. - """ - - total_endorsements: int = pydantic.Field() - """ - Total endorsement rows for this entity. 0 when none. - """ - - unique_channels: int = pydantic.Field() - """ - Number of distinct channels with commercial mentions of this entity. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_topic_list_response.py b/src/arcmira/types/entity_topic_list_response.py deleted file mode 100644 index 2bd42d0..0000000 --- a/src/arcmira/types/entity_topic_list_response.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_topic_list_response_export_capabilities import EntityTopicListResponseExportCapabilities -from .entity_topic_list_response_items_item import EntityTopicListResponseItemsItem -from .exposure_meta import ExposureMeta - - -class EntityTopicListResponse(UniversalBaseModel): - items: typing.List[EntityTopicListResponseItemsItem] = pydantic.Field() - """ - Topics that co-occur with the entity in indexed media, highest count first by default. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - export_capabilities: typing_extensions.Annotated[ - EntityTopicListResponseExportCapabilities, - FieldMetadata(alias="exportCapabilities"), - pydantic.Field( - alias="exportCapabilities", - description="Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts.", - ), - ] - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_topic_list_response_export_capabilities.py b/src/arcmira/types/entity_topic_list_response_export_capabilities.py deleted file mode 100644 index e37b677..0000000 --- a/src/arcmira/types/entity_topic_list_response_export_capabilities.py +++ /dev/null @@ -1,69 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class EntityTopicListResponseExportCapabilities(UniversalBaseModel): - """ - Which orderings and filters this list can serve right now. A list served from a stored top-N serves only the highest counts. - """ - - highest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="highestCount"), - pydantic.Field( - alias="highestCount", description="Always true: the highest-count ordering is always available." - ), - ] - """ - Always true: the highest-count ordering is always available. - """ - - lowest_count: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="lowestCount"), - pydantic.Field(alias="lowestCount", description="True when the lowest-count ordering is available."), - ] - """ - True when the lowest-count ordering is available. - """ - - alternate_sort: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="alternateSort"), - pydantic.Field(alias="alternateSort", description="True when sort keys other than count are available."), - ] - """ - True when sort keys other than count are available. - """ - - search: bool = pydantic.Field() - """ - True when the q filter is available. - """ - - available_rows: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="availableRows"), - pydantic.Field( - alias="availableRows", - description="Present only when a stored top-N is smaller than the full result set: the rows it holds.", - ), - ] = None - """ - Present only when a stored top-N is smaller than the full result set: the rows it holds. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_topic_list_response_items_item.py b/src/arcmira/types/entity_topic_list_response_items_item.py deleted file mode 100644 index f6d7752..0000000 --- a/src/arcmira/types/entity_topic_list_response_items_item.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_topic_list_response_items_item_sentiment import EntityTopicListResponseItemsItemSentiment - - -class EntityTopicListResponseItemsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id of the topic. - """ - - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Distinct media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: EntityTopicListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_topic_list_response_items_item_sentiment.py b/src/arcmira/types/entity_topic_list_response_items_item_sentiment.py deleted file mode 100644 index d2d8191..0000000 --- a/src/arcmira/types/entity_topic_list_response_items_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityTopicListResponseItemsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/error.py b/src/arcmira/types/error.py index 026ad51..265020b 100644 --- a/src/arcmira/types/error.py +++ b/src/arcmira/types/error.py @@ -3,36 +3,11 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata from .error_error import ErrorError -from .error_quote import ErrorQuote class Error(UniversalBaseModel): - quote: typing.Optional[ErrorQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - - existing_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="existingId"), - pydantic.Field( - alias="existingId", - description="On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker.", - ), - ] = None - """ - On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. - """ - error: ErrorError if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/error_error.py b/src/arcmira/types/error_error.py index f766511..d9d11c9 100644 --- a/src/arcmira/types/error_error.py +++ b/src/arcmira/types/error_error.py @@ -4,6 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .error_error_details import ErrorErrorDetails from .error_error_gate import ErrorErrorGate from .error_error_reason import ErrorErrorReason from .error_error_type import ErrorErrorType @@ -63,6 +64,11 @@ class ErrorError(UniversalBaseModel): On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. """ + details: typing.Optional[ErrorErrorDetails] = pydantic.Field(default=None) + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + doc_url: str request_id: str diff --git a/src/arcmira/types/error_error_details.py b/src/arcmira/types/error_error_details.py new file mode 100644 index 0000000..5b16cf8 --- /dev/null +++ b/src/arcmira/types/error_error_details.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .error_error_details_quote import ErrorErrorDetailsQuote + + +class ErrorErrorDetails(UniversalBaseModel): + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + + quote: typing.Optional[ErrorErrorDetailsQuote] = pydantic.Field(default=None) + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + existing_request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. + """ + + existing_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/error_quote.py b/src/arcmira/types/error_error_details_quote.py similarity index 82% rename from src/arcmira/types/error_quote.py rename to src/arcmira/types/error_error_details_quote.py index 7b40432..4edd8c2 100644 --- a/src/arcmira/types/error_quote.py +++ b/src/arcmira/types/error_error_details_quote.py @@ -4,16 +4,16 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .error_quote_charge import ErrorQuoteCharge +from .error_error_details_quote_charge import ErrorErrorDetailsQuoteCharge from .transcript_quote import TranscriptQuote -class ErrorQuote(TranscriptQuote): +class ErrorErrorDetailsQuote(TranscriptQuote): """ The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. """ - charge: typing.Optional[ErrorQuoteCharge] = pydantic.Field(default=None) + charge: typing.Optional[ErrorErrorDetailsQuoteCharge] = pydantic.Field(default=None) """ What the purchase would charge at the current balance. Absent when no current price could be read. """ diff --git a/src/arcmira/types/error_quote_charge.py b/src/arcmira/types/error_error_details_quote_charge.py similarity index 75% rename from src/arcmira/types/error_quote_charge.py rename to src/arcmira/types/error_error_details_quote_charge.py index 42b5433..ce45bc0 100644 --- a/src/arcmira/types/error_quote_charge.py +++ b/src/arcmira/types/error_error_details_quote_charge.py @@ -6,19 +6,19 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .error_quote_charge_from import ErrorQuoteChargeFrom -from .error_quote_charge_unit import ErrorQuoteChargeUnit +from .error_error_details_quote_charge_from import ErrorErrorDetailsQuoteChargeFrom +from .error_error_details_quote_charge_unit import ErrorErrorDetailsQuoteChargeUnit -class ErrorQuoteCharge(UniversalBaseModel): +class ErrorErrorDetailsQuoteCharge(UniversalBaseModel): """ What the purchase would charge at the current balance. Absent when no current price could be read. """ - unit: ErrorQuoteChargeUnit + unit: ErrorErrorDetailsQuoteChargeUnit amount: float from_: typing_extensions.Annotated[ - ErrorQuoteChargeFrom, + ErrorErrorDetailsQuoteChargeFrom, FieldMetadata(alias="from"), pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), ] diff --git a/src/arcmira/types/error_error_details_quote_charge_from.py b/src/arcmira/types/error_error_details_quote_charge_from.py new file mode 100644 index 0000000..d5f7f9d --- /dev/null +++ b/src/arcmira/types/error_error_details_quote_charge_from.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ErrorErrorDetailsQuoteChargeFrom = typing.Union[typing.Literal["included", "on_demand", "mixed"], typing.Any] diff --git a/src/arcmira/types/error_error_details_quote_charge_unit.py b/src/arcmira/types/error_error_details_quote_charge_unit.py new file mode 100644 index 0000000..66de813 --- /dev/null +++ b/src/arcmira/types/error_error_details_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +ErrorErrorDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/error_quote_charge_from.py b/src/arcmira/types/error_quote_charge_from.py deleted file mode 100644 index b8ae461..0000000 --- a/src/arcmira/types/error_quote_charge_from.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ErrorQuoteChargeFrom = typing.Union[typing.Literal["included", "on_demand", "mixed"], typing.Any] diff --git a/src/arcmira/types/error_quote_charge_unit.py b/src/arcmira/types/error_quote_charge_unit.py deleted file mode 100644 index 1368a48..0000000 --- a/src/arcmira/types/error_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ErrorQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/exposure_meta.py b/src/arcmira/types/exposure_meta.py deleted file mode 100644 index 2ca89dd..0000000 --- a/src/arcmira/types/exposure_meta.py +++ /dev/null @@ -1,313 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_access import ExposureMetaAccess -from .exposure_meta_credits import ExposureMetaCredits -from .exposure_meta_free_limit import ExposureMetaFreeLimit -from .exposure_meta_limit_action import ExposureMetaLimitAction -from .exposure_meta_limits import ExposureMetaLimits -from .exposure_meta_recent_preview import ExposureMetaRecentPreview -from .exposure_meta_recent_preview_mentions import ExposureMetaRecentPreviewMentions -from .exposure_meta_totals import ExposureMetaTotals -from .exposure_meta_usage_limit_type import ExposureMetaUsageLimitType - - -class ExposureMeta(UniversalBaseModel): - """ - Usage, plan limits, and the access block for this response: what the caller's plan served and what it withheld. - """ - - tier: str = pydantic.Field() - """ - Plan tier of the caller. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise. - """ - - is_authenticated: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isAuthenticated"), - pydantic.Field(alias="isAuthenticated", description="True when the request carried a valid credential."), - ] - """ - True when the request carried a valid credential. - """ - - rows_used: typing_extensions.Annotated[ - float, - FieldMetadata(alias="rowsUsed"), - pydantic.Field(alias="rowsUsed", description="Rows consumed this period, or lifetime rows on the free tier."), - ] - """ - Rows consumed this period, or lifetime rows on the free tier. - """ - - rows_remaining: typing_extensions.Annotated[ - float, - FieldMetadata(alias="rowsRemaining"), - pydantic.Field( - alias="rowsRemaining", description="Rows left this period, or lifetime rows left on the free tier." - ), - ] - """ - Rows left this period, or lifetime rows left on the free tier. - """ - - monthly_rows: typing_extensions.Annotated[ - float, - FieldMetadata(alias="monthlyRows"), - pydantic.Field( - alias="monthlyRows", description="Rows included per period, or the lifetime allocation on the free tier." - ), - ] - """ - Rows included per period, or the lifetime allocation on the free tier. - """ - - is_over_limit: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isOverLimit"), - pydantic.Field(alias="isOverLimit", description="True when rowsUsed has reached monthlyRows."), - ] - """ - True when rowsUsed has reached monthlyRows. - """ - - on_demand_enabled: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="onDemandEnabled"), - pydantic.Field( - alias="onDemandEnabled", description="True when on-demand usage past the included rows is enabled." - ), - ] - """ - True when on-demand usage past the included rows is enabled. - """ - - spend_limit_cents: typing_extensions.Annotated[ - float, - FieldMetadata(alias="spendLimitCents"), - pydantic.Field(alias="spendLimitCents", description="On-demand spend limit in US cents. 0 means unlimited."), - ] - """ - On-demand spend limit in US cents. 0 means unlimited. - """ - - current_spend_cents: typing_extensions.Annotated[ - float, - FieldMetadata(alias="currentSpendCents"), - pydantic.Field(alias="currentSpendCents", description="On-demand spend so far this period, in US cents."), - ] - """ - On-demand spend so far this period, in US cents. - """ - - can_continue: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="canContinue"), - pydantic.Field(alias="canContinue", description="True when the caller can keep reading data right now."), - ] - """ - True when the caller can keep reading data right now. - """ - - showing_full_data: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="showingFullData"), - pydantic.Field( - alias="showingFullData", - description="True when the plan serves full data. False when the response is a preview: counts are null and lists are cut.", - ), - ] - """ - True when the plan serves full data. False when the response is a preview: counts are null and lists are cut. - """ - - usage_limit_type: typing_extensions.Annotated[ - ExposureMetaUsageLimitType, - FieldMetadata(alias="usageLimitType"), - pydantic.Field( - alias="usageLimitType", - description="lifetime on the free tier, whose rows never reset. monthly on paid tiers.", - ), - ] - """ - lifetime on the free tier, whose rows never reset. monthly on paid tiers. - """ - - lifetime_rows_used: typing_extensions.Annotated[ - typing.Optional[float], - FieldMetadata(alias="lifetimeRowsUsed"), - pydantic.Field( - alias="lifetimeRowsUsed", description="Free tier only: lifetime rows used. Absent on paid tiers." - ), - ] = None - """ - Free tier only: lifetime rows used. Absent on paid tiers. - """ - - lifetime_rows_allocated: typing_extensions.Annotated[ - typing.Optional[float], - FieldMetadata(alias="lifetimeRowsAllocated"), - pydantic.Field( - alias="lifetimeRowsAllocated", description="Free tier only: lifetime rows allocated. Absent on paid tiers." - ), - ] = None - """ - Free tier only: lifetime rows allocated. Absent on paid tiers. - """ - - limit_action: typing_extensions.Annotated[ - typing.Optional[ExposureMetaLimitAction], - FieldMetadata(alias="limitAction"), - pydantic.Field( - alias="limitAction", - description="Present only when the caller is over the limit and cannot continue. Names the fix: upgrade_to_pro, upgrade_or_enable_ondemand, upgrade_or_increase_limit, enable_ondemand, increase_limit, or contact_sales.", - ), - ] = None - """ - Present only when the caller is over the limit and cannot continue. Names the fix: upgrade_to_pro, upgrade_or_enable_ondemand, upgrade_or_increase_limit, enable_ondemand, increase_limit, or contact_sales. - """ - - limit_message: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="limitMessage"), - pydantic.Field( - alias="limitMessage", description="Human sentence for the limit banner. Present only alongside limitAction." - ), - ] = None - """ - Human sentence for the limit banner. Present only alongside limitAction. - """ - - limit_cta_href: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="limitCtaHref"), - pydantic.Field( - alias="limitCtaHref", - description="Site path for the limit banner button. Present only alongside limitAction.", - ), - ] = None - """ - Site path for the limit banner button. Present only alongside limitAction. - """ - - limit_upgrade_tier: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="limitUpgradeTier"), - pydantic.Field( - alias="limitUpgradeTier", - description="The next tier that lifts the limit. Present only alongside limitAction when an upgrade exists. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise.", - ), - ] = None - """ - The next tier that lifts the limit. Present only alongside limitAction when an upgrade exists. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise. - """ - - limit_upgrade_tier_name: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="limitUpgradeTierName"), - pydantic.Field(alias="limitUpgradeTierName", description="Display name of limitUpgradeTier."), - ] = None - """ - Display name of limitUpgradeTier. - """ - - credits: typing.Optional[ExposureMetaCredits] = pydantic.Field(default=None) - """ - The month in credits (1 credit is $0.001; a row is 4 credits). Present only when the credits ledger decides access. - """ - - totals: ExposureMetaTotals = pydantic.Field() - """ - True totals behind the returned rows. Each response sets only the keys for its own sections. - """ - - limits: ExposureMetaLimits = pydantic.Field() - """ - Anonymous row limits and the plan's feature flags. - """ - - cost_per_row_cents: typing_extensions.Annotated[ - float, - FieldMetadata(alias="costPerRowCents"), - pydantic.Field( - alias="costPerRowCents", - description="On-demand price per row in US cents, e.g. 0.4. 0 on tiers without on-demand.", - ), - ] - """ - On-demand price per row in US cents, e.g. 0.4. 0 on tiers without on-demand. - """ - - is_overage: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isOverage"), - pydantic.Field( - alias="isOverage", description="True when this request billed rows past the included allowance." - ), - ] - """ - True when this request billed rows past the included allowance. - """ - - free_limit: typing_extensions.Annotated[ - ExposureMetaFreeLimit, - FieldMetadata(alias="freeLimit"), - pydantic.Field(alias="freeLimit", description="The anonymous row limits again, under their older key."), - ] - """ - The anonymous row limits again, under their older key. - """ - - recent_preview: typing_extensions.Annotated[ - typing.Optional[ExposureMetaRecentPreview], - FieldMetadata(alias="recentPreview"), - pydantic.Field( - alias="recentPreview", - description="Present when the plan withholds the newest media: how much is hidden and a few safe teaser rows.", - ), - ] = None - """ - Present when the plan withholds the newest media: how much is hidden and a few safe teaser rows. - """ - - recent_preview_mentions: typing_extensions.Annotated[ - typing.Optional[ExposureMetaRecentPreviewMentions], - FieldMetadata(alias="recentPreviewMentions"), - pydantic.Field( - alias="recentPreviewMentions", description="Person pages only: the same band for the mentions list." - ), - ] = None - """ - Person pages only: the same band for the mentions list. - """ - - access: ExposureMetaAccess = pydantic.Field() - """ - Structured access block: what the plan served and what it withheld. - """ - - dc: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="_dc"), - pydantic.Field( - alias="_dc", - description="Delivery class code the freshness filter ran at. Present only when the plan delays media and the response went through that filter.", - ), - ] = None - """ - Delivery class code the freshness filter ran at. Present only when the plan delays media and the response went through that filter. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access.py b/src/arcmira/types/exposure_meta_access.py deleted file mode 100644 index 5447552..0000000 --- a/src/arcmira/types/exposure_meta_access.py +++ /dev/null @@ -1,92 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_access_chart import ExposureMetaAccessChart -from .exposure_meta_access_cls import ExposureMetaAccessCls -from .exposure_meta_access_freshness import ExposureMetaAccessFreshness -from .exposure_meta_access_ladder import ExposureMetaAccessLadder -from .exposure_meta_access_rows import ExposureMetaAccessRows -from .exposure_meta_access_unlock import ExposureMetaAccessUnlock -from .exposure_meta_access_view import ExposureMetaAccessView -from .exposure_meta_access_withheld_item import ExposureMetaAccessWithheldItem - - -class ExposureMetaAccess(UniversalBaseModel): - """ - Structured access block: what the plan served and what it withheld. - """ - - v: float = pydantic.Field() - """ - Access block version. Always 1. - """ - - view: ExposureMetaAccessView = pydantic.Field() - """ - full when the plan serves everything; preview when something was cut. - """ - - cls: ExposureMetaAccessCls = pydantic.Field() - """ - Freshness class of the response. live serves the newest media; the others delay it. - """ - - ladder: ExposureMetaAccessLadder = pydantic.Field() - """ - Where the caller stands on the gate ladder. - """ - - rows: ExposureMetaAccessRows = pydantic.Field() - """ - Rows served, visible, and blurred per section. - """ - - freshness: ExposureMetaAccessFreshness = pydantic.Field() - """ - The freshness gate applied. - """ - - chart: ExposureMetaAccessChart = pydantic.Field() - """ - The timeline the plan serves. - """ - - pagination: bool = pydantic.Field() - """ - True when the plan may page past the first page. - """ - - show_counts: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="showCounts"), - pydantic.Field( - alias="showCounts", description="True when the plan serves counts. When false, row counts are null." - ), - ] - """ - True when the plan serves counts. When false, row counts are null. - """ - - withheld: typing.List[ExposureMetaAccessWithheldItem] = pydantic.Field() - """ - Every resource the plan withheld from this response. Empty when view is full. - """ - - unlock: typing.Optional[ExposureMetaAccessUnlock] = pydantic.Field(default=None) - """ - How to lift the gate. Null when view is full. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_chart.py b/src/arcmira/types/exposure_meta_access_chart.py deleted file mode 100644 index 98ba7e4..0000000 --- a/src/arcmira/types/exposure_meta_access_chart.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaAccessChart(UniversalBaseModel): - """ - The timeline the plan serves. - """ - - months: int = pydantic.Field() - """ - Months of timeline the plan serves. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_cls.py b/src/arcmira/types/exposure_meta_access_cls.py deleted file mode 100644 index f3b01b6..0000000 --- a/src/arcmira/types/exposure_meta_access_cls.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessCls = typing.Union[typing.Literal["live", "warm", "public", "cold"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_freshness.py b/src/arcmira/types/exposure_meta_access_freshness.py deleted file mode 100644 index 0e51901..0000000 --- a/src/arcmira/types/exposure_meta_access_freshness.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ExposureMetaAccessFreshness(UniversalBaseModel): - """ - The freshness gate applied. - """ - - code: int = pydantic.Field() - """ - Delivery class code: 0 live, 1 to 3 delayed classes. - """ - - delay_days: typing_extensions.Annotated[ - int, - FieldMetadata(alias="delayDays"), - pydantic.Field(alias="delayDays", description="Days of newest media withheld. 0 when live."), - ] - """ - Days of newest media withheld. 0 when live. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_ladder.py b/src/arcmira/types/exposure_meta_access_ladder.py deleted file mode 100644 index f5d3a00..0000000 --- a/src/arcmira/types/exposure_meta_access_ladder.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessLadder = typing.Union[typing.Literal["anonymous", "free", "usage_limit", "unlocked"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_rows.py b/src/arcmira/types/exposure_meta_access_rows.py deleted file mode 100644 index d95d534..0000000 --- a/src/arcmira/types/exposure_meta_access_rows.py +++ /dev/null @@ -1,39 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .exposure_meta_access_rows_entities import ExposureMetaAccessRowsEntities -from .exposure_meta_access_rows_media import ExposureMetaAccessRowsMedia -from .exposure_meta_access_rows_topics import ExposureMetaAccessRowsTopics - - -class ExposureMetaAccessRows(UniversalBaseModel): - """ - Rows served, visible, and blurred per section. - """ - - media: ExposureMetaAccessRowsMedia = pydantic.Field() - """ - Media rows. - """ - - topics: ExposureMetaAccessRowsTopics = pydantic.Field() - """ - Topic sidebar rows. - """ - - entities: ExposureMetaAccessRowsEntities = pydantic.Field() - """ - People, organization, product, and channel sidebar rows. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_rows_entities.py b/src/arcmira/types/exposure_meta_access_rows_entities.py deleted file mode 100644 index 4a49463..0000000 --- a/src/arcmira/types/exposure_meta_access_rows_entities.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaAccessRowsEntities(UniversalBaseModel): - """ - People, organization, product, and channel sidebar rows. - """ - - served: int = pydantic.Field() - """ - Rows the backend returns for this section. - """ - - visible: int = pydantic.Field() - """ - Rows the site paints unobscured. - """ - - placeholders: int = pydantic.Field() - """ - Blurred rows the site paints after the visible ones. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_rows_media.py b/src/arcmira/types/exposure_meta_access_rows_media.py deleted file mode 100644 index e8b9eba..0000000 --- a/src/arcmira/types/exposure_meta_access_rows_media.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaAccessRowsMedia(UniversalBaseModel): - """ - Media rows. - """ - - served: int = pydantic.Field() - """ - Rows the backend returns for this section. - """ - - visible: int = pydantic.Field() - """ - Rows the site paints unobscured. - """ - - placeholders: int = pydantic.Field() - """ - Blurred rows the site paints after the visible ones. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_rows_topics.py b/src/arcmira/types/exposure_meta_access_rows_topics.py deleted file mode 100644 index 1707f43..0000000 --- a/src/arcmira/types/exposure_meta_access_rows_topics.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaAccessRowsTopics(UniversalBaseModel): - """ - Topic sidebar rows. - """ - - served: int = pydantic.Field() - """ - Rows the backend returns for this section. - """ - - visible: int = pydantic.Field() - """ - Rows the site paints unobscured. - """ - - placeholders: int = pydantic.Field() - """ - Blurred rows the site paints after the visible ones. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_unlock.py b/src/arcmira/types/exposure_meta_access_unlock.py deleted file mode 100644 index 306d172..0000000 --- a/src/arcmira/types/exposure_meta_access_unlock.py +++ /dev/null @@ -1,51 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_access_unlock_limit_action import ExposureMetaAccessUnlockLimitAction -from .exposure_meta_access_unlock_src import ExposureMetaAccessUnlockSrc - - -class ExposureMetaAccessUnlock(UniversalBaseModel): - """ - How to lift the gate. Null when view is full. - """ - - tier: str = pydantic.Field() - """ - The tier that removes the gate. Values: anonymous, free, hobby, pro, pro_plus, ultra, teams, enterprise. - """ - - url: str = pydantic.Field() - """ - Absolute upgrade or sign-up URL carrying its ?src= attribution. - """ - - src: ExposureMetaAccessUnlockSrc = pydantic.Field() - """ - The surface the unlock link attributes to. - """ - - limit_action: typing_extensions.Annotated[ - typing.Optional[ExposureMetaAccessUnlockLimitAction], - FieldMetadata(alias="limitAction"), - pydantic.Field( - alias="limitAction", description="The limit fix when the caller is over its limit. Null otherwise." - ), - ] = None - """ - The limit fix when the caller is over its limit. Null otherwise. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_unlock_limit_action.py b/src/arcmira/types/exposure_meta_access_unlock_limit_action.py deleted file mode 100644 index c7bf8ee..0000000 --- a/src/arcmira/types/exposure_meta_access_unlock_limit_action.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessUnlockLimitAction = typing.Union[ - typing.Literal[ - "upgrade_to_pro", - "upgrade_or_enable_ondemand", - "upgrade_or_increase_limit", - "enable_ondemand", - "increase_limit", - "contact_sales", - ], - typing.Any, -] diff --git a/src/arcmira/types/exposure_meta_access_unlock_src.py b/src/arcmira/types/exposure_meta_access_unlock_src.py deleted file mode 100644 index f38936e..0000000 --- a/src/arcmira/types/exposure_meta_access_unlock_src.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessUnlockSrc = typing.Union[typing.Literal["access-envelope", "api-boundary", "mcp-tool"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_view.py b/src/arcmira/types/exposure_meta_access_view.py deleted file mode 100644 index 965865a..0000000 --- a/src/arcmira/types/exposure_meta_access_view.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessView = typing.Union[typing.Literal["full", "preview"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_withheld_item.py b/src/arcmira/types/exposure_meta_access_withheld_item.py deleted file mode 100644 index 92b95a0..0000000 --- a/src/arcmira/types/exposure_meta_access_withheld_item.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_access_withheld_item_kind import ExposureMetaAccessWithheldItemKind -from .exposure_meta_access_withheld_item_param import ExposureMetaAccessWithheldItemParam -from .exposure_meta_access_withheld_item_section import ExposureMetaAccessWithheldItemSection -from .exposure_meta_access_withheld_item_what import ExposureMetaAccessWithheldItemWhat - - -class ExposureMetaAccessWithheldItem(UniversalBaseModel): - kind: ExposureMetaAccessWithheldItemKind = pydantic.Field() - """ - What was withheld. Values: media_rows (media rows past a position), fresh_media (the newest media), sidebar_rows (sidebar rows past a position), counts (row counts), chart (the timeline), pagination (pages past the first). - """ - - what: ExposureMetaAccessWithheldItemWhat = pydantic.Field() - """ - Repeats kind, for readers written against the older key. - """ - - beyond_row: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="beyondRow"), - pydantic.Field( - alias="beyondRow", description="media_rows and sidebar_rows only: rows past this position are withheld." - ), - ] = None - """ - media_rows and sidebar_rows only: rows past this position are withheld. - """ - - window_days: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="windowDays"), - pydantic.Field(alias="windowDays", description="fresh_media only: days of newest media withheld."), - ] = None - """ - fresh_media only: days of newest media withheld. - """ - - cutoff: typing.Optional[str] = pydantic.Field(default=None) - """ - fresh_media only: the publish date cutoff. Null when none applies. - """ - - section: typing.Optional[ExposureMetaAccessWithheldItemSection] = pydantic.Field(default=None) - """ - sidebar_rows only: which sidebar section. - """ - - param: typing.Optional[ExposureMetaAccessWithheldItemParam] = pydantic.Field(default=None) - """ - pagination only: the paging parameter that was refused. Null when none was sent. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_access_withheld_item_kind.py b/src/arcmira/types/exposure_meta_access_withheld_item_kind.py deleted file mode 100644 index d2984c3..0000000 --- a/src/arcmira/types/exposure_meta_access_withheld_item_kind.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessWithheldItemKind = typing.Union[ - typing.Literal["media_rows", "fresh_media", "sidebar_rows", "counts", "chart", "pagination"], typing.Any -] diff --git a/src/arcmira/types/exposure_meta_access_withheld_item_param.py b/src/arcmira/types/exposure_meta_access_withheld_item_param.py deleted file mode 100644 index dc0f561..0000000 --- a/src/arcmira/types/exposure_meta_access_withheld_item_param.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessWithheldItemParam = typing.Union[typing.Literal["offset", "cursor"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_withheld_item_section.py b/src/arcmira/types/exposure_meta_access_withheld_item_section.py deleted file mode 100644 index 904a3a8..0000000 --- a/src/arcmira/types/exposure_meta_access_withheld_item_section.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessWithheldItemSection = typing.Union[typing.Literal["topics", "entities"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_access_withheld_item_what.py b/src/arcmira/types/exposure_meta_access_withheld_item_what.py deleted file mode 100644 index a08db50..0000000 --- a/src/arcmira/types/exposure_meta_access_withheld_item_what.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaAccessWithheldItemWhat = typing.Union[ - typing.Literal["media_rows", "fresh_media", "sidebar_rows", "counts", "chart", "pagination"], typing.Any -] diff --git a/src/arcmira/types/exposure_meta_credits.py b/src/arcmira/types/exposure_meta_credits.py deleted file mode 100644 index 46a3bad..0000000 --- a/src/arcmira/types/exposure_meta_credits.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .exposure_meta_credits_on_demand import ExposureMetaCreditsOnDemand -from .exposure_meta_credits_plan import ExposureMetaCreditsPlan - - -class ExposureMetaCredits(UniversalBaseModel): - """ - The month in credits (1 credit is $0.001; a row is 4 credits). Present only when the credits ledger decides access. - """ - - available: typing.Optional[int] = pydantic.Field(default=None) - """ - Credits spendable now: plan, granted, purchased, and on-demand up to its cap. Null when nothing limits it. - """ - - plan: ExposureMetaCreditsPlan - granted: int = pydantic.Field() - """ - Credits left in granted lots that have not expired. - """ - - purchased: int = pydantic.Field() - """ - Credits left in purchased top-ups. - """ - - on_demand: ExposureMetaCreditsOnDemand - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_credits_on_demand.py b/src/arcmira/types/exposure_meta_credits_on_demand.py deleted file mode 100644 index 894e0e8..0000000 --- a/src/arcmira/types/exposure_meta_credits_on_demand.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaCreditsOnDemand(UniversalBaseModel): - enabled: bool = pydantic.Field() - """ - True when on-demand credits are on. - """ - - cap_credits: typing.Optional[int] = pydantic.Field(default=None) - """ - The on-demand cap in credits, at $0.002 a credit. Null when uncapped or off. - """ - - used: int = pydantic.Field() - """ - On-demand credits spent this month. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_credits_plan.py b/src/arcmira/types/exposure_meta_credits_plan.py deleted file mode 100644 index a04df7e..0000000 --- a/src/arcmira/types/exposure_meta_credits_plan.py +++ /dev/null @@ -1,32 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaCreditsPlan(UniversalBaseModel): - credits: typing.Optional[int] = pydantic.Field(default=None) - """ - Plan credits this month. Null when the plan has no limit. - """ - - used: int = pydantic.Field() - """ - Plan credits spent this month. - """ - - resets_at: str = pydantic.Field() - """ - YYYY-MM-DD, the first day of next month (UTC), when plan credits reset. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_limit_action.py b/src/arcmira/types/exposure_meta_limit_action.py deleted file mode 100644 index 683f1a2..0000000 --- a/src/arcmira/types/exposure_meta_limit_action.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaLimitAction = typing.Union[ - typing.Literal[ - "upgrade_to_pro", - "upgrade_or_enable_ondemand", - "upgrade_or_increase_limit", - "enable_ondemand", - "increase_limit", - "contact_sales", - ], - typing.Any, -] diff --git a/src/arcmira/types/exposure_meta_limits.py b/src/arcmira/types/exposure_meta_limits.py deleted file mode 100644 index d74a4da..0000000 --- a/src/arcmira/types/exposure_meta_limits.py +++ /dev/null @@ -1,70 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ExposureMetaLimits(UniversalBaseModel): - """ - Anonymous row limits and the plan's feature flags. - """ - - free_appearances: typing_extensions.Annotated[ - int, - FieldMetadata(alias="freeAppearances"), - pydantic.Field(alias="freeAppearances", description="Media rows an anonymous caller is served."), - ] - """ - Media rows an anonymous caller is served. - """ - - free_entities_per_type: typing_extensions.Annotated[ - int, - FieldMetadata(alias="freeEntitiesPerType"), - pydantic.Field( - alias="freeEntitiesPerType", description="Sidebar rows per entity type an anonymous caller is served." - ), - ] - """ - Sidebar rows per entity type an anonymous caller is served. - """ - - webhooks_enabled: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="webhooksEnabled"), - pydantic.Field(alias="webhooksEnabled", description="True when the plan includes webhook delivery."), - ] - """ - True when the plan includes webhook delivery. - """ - - export_enabled: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="exportEnabled"), - pydantic.Field(alias="exportEnabled", description="True when the plan includes export."), - ] - """ - True when the plan includes export. - """ - - api_enabled: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="apiEnabled"), - pydantic.Field(alias="apiEnabled", description="True when the plan includes API access."), - ] - """ - True when the plan includes API access. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_recent_preview.py b/src/arcmira/types/exposure_meta_recent_preview.py deleted file mode 100644 index c6fe416..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview.py +++ /dev/null @@ -1,89 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_recent_preview_experiment import ExposureMetaRecentPreviewExperiment -from .exposure_meta_recent_preview_subject import ExposureMetaRecentPreviewSubject -from .exposure_meta_recent_preview_teaser_items_item import ExposureMetaRecentPreviewTeaserItemsItem - - -class ExposureMetaRecentPreview(UniversalBaseModel): - """ - Present when the plan withholds the newest media: how much is hidden and a few safe teaser rows. - """ - - window_days: typing_extensions.Annotated[ - int, - FieldMetadata(alias="windowDays"), - pydantic.Field(alias="windowDays", description="Days of newest media the plan withholds."), - ] - """ - Days of newest media the plan withholds. - """ - - hidden_recent_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="hiddenRecentCount"), - pydantic.Field(alias="hiddenRecentCount", description="Media rows inside the withheld window."), - ] - """ - Media rows inside the withheld window. - """ - - free_account_unlock_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="freeAccountUnlockCount"), - pydantic.Field( - alias="freeAccountUnlockCount", - description="Rows a free account would unlock. Present only for anonymous callers (freshness code 2).", - ), - ] = None - """ - Rows a free account would unlock. Present only for anonymous callers (freshness code 2). - """ - - newest_hidden_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="newestHiddenAt"), - pydantic.Field( - alias="newestHiddenAt", - description="Newest publish date inside the withheld window. Absent when nothing is withheld.", - ), - ] = None - """ - Newest publish date inside the withheld window. Absent when nothing is withheld. - """ - - teaser_items: typing_extensions.Annotated[ - typing.Optional[typing.List[ExposureMetaRecentPreviewTeaserItemsItem]], - FieldMetadata(alias="teaserItems"), - pydantic.Field( - alias="teaserItems", description="Up to three of the newest withheld rows, with safe fields only." - ), - ] = None - """ - Up to three of the newest withheld rows, with safe fields only. - """ - - subject: ExposureMetaRecentPreviewSubject = pydantic.Field() - """ - Which list the withheld rows belong to. - """ - - experiment: ExposureMetaRecentPreviewExperiment = pydantic.Field() - """ - The copy experiment this band feeds. Always recent-intel-gate-copy-v1. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_recent_preview_experiment.py b/src/arcmira/types/exposure_meta_recent_preview_experiment.py deleted file mode 100644 index 2f3d4b2..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_experiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaRecentPreviewExperiment = typing.Union[typing.Literal["recent-intel-gate-copy-v1"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_recent_preview_mentions.py b/src/arcmira/types/exposure_meta_recent_preview_mentions.py deleted file mode 100644 index 30d6b8a..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_mentions.py +++ /dev/null @@ -1,89 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta_recent_preview_mentions_experiment import ExposureMetaRecentPreviewMentionsExperiment -from .exposure_meta_recent_preview_mentions_subject import ExposureMetaRecentPreviewMentionsSubject -from .exposure_meta_recent_preview_mentions_teaser_items_item import ExposureMetaRecentPreviewMentionsTeaserItemsItem - - -class ExposureMetaRecentPreviewMentions(UniversalBaseModel): - """ - Person pages only: the same band for the mentions list. - """ - - window_days: typing_extensions.Annotated[ - int, - FieldMetadata(alias="windowDays"), - pydantic.Field(alias="windowDays", description="Days of newest media the plan withholds."), - ] - """ - Days of newest media the plan withholds. - """ - - hidden_recent_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="hiddenRecentCount"), - pydantic.Field(alias="hiddenRecentCount", description="Media rows inside the withheld window."), - ] - """ - Media rows inside the withheld window. - """ - - free_account_unlock_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="freeAccountUnlockCount"), - pydantic.Field( - alias="freeAccountUnlockCount", - description="Rows a free account would unlock. Present only for anonymous callers (freshness code 2).", - ), - ] = None - """ - Rows a free account would unlock. Present only for anonymous callers (freshness code 2). - """ - - newest_hidden_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="newestHiddenAt"), - pydantic.Field( - alias="newestHiddenAt", - description="Newest publish date inside the withheld window. Absent when nothing is withheld.", - ), - ] = None - """ - Newest publish date inside the withheld window. Absent when nothing is withheld. - """ - - teaser_items: typing_extensions.Annotated[ - typing.Optional[typing.List[ExposureMetaRecentPreviewMentionsTeaserItemsItem]], - FieldMetadata(alias="teaserItems"), - pydantic.Field( - alias="teaserItems", description="Up to three of the newest withheld rows, with safe fields only." - ), - ] = None - """ - Up to three of the newest withheld rows, with safe fields only. - """ - - subject: ExposureMetaRecentPreviewMentionsSubject = pydantic.Field() - """ - Which list the withheld rows belong to. - """ - - experiment: ExposureMetaRecentPreviewMentionsExperiment = pydantic.Field() - """ - The copy experiment this band feeds. Always recent-intel-gate-copy-v1. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_recent_preview_mentions_experiment.py b/src/arcmira/types/exposure_meta_recent_preview_mentions_experiment.py deleted file mode 100644 index 24f719a..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_mentions_experiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaRecentPreviewMentionsExperiment = typing.Union[typing.Literal["recent-intel-gate-copy-v1"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_recent_preview_mentions_subject.py b/src/arcmira/types/exposure_meta_recent_preview_mentions_subject.py deleted file mode 100644 index 3d70654..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_mentions_subject.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaRecentPreviewMentionsSubject = typing.Union[typing.Literal["appearances", "mentions", "items"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_recent_preview_mentions_teaser_items_item.py b/src/arcmira/types/exposure_meta_recent_preview_mentions_teaser_items_item.py deleted file mode 100644 index 5f27e22..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_mentions_teaser_items_item.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ExposureMetaRecentPreviewMentionsTeaserItemsItem(UniversalBaseModel): - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL."), - ] - """ - Video thumbnail URL. - """ - - channel_name: typing_extensions.Annotated[ - str, FieldMetadata(alias="channelName"), pydantic.Field(alias="channelName", description="Source channel name.") - ] - """ - Source channel name. - """ - - published_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish date of the withheld video."), - ] - """ - Publish date of the withheld video. - """ - - duration: typing.Optional[str] = pydantic.Field(default=None) - """ - Video length as display text. Null when unknown. - """ - - title: str = pydantic.Field() - """ - Video title, truncated server side. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_recent_preview_subject.py b/src/arcmira/types/exposure_meta_recent_preview_subject.py deleted file mode 100644 index 04b5ac0..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_subject.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaRecentPreviewSubject = typing.Union[typing.Literal["appearances", "mentions", "items"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_recent_preview_teaser_items_item.py b/src/arcmira/types/exposure_meta_recent_preview_teaser_items_item.py deleted file mode 100644 index 5d4c736..0000000 --- a/src/arcmira/types/exposure_meta_recent_preview_teaser_items_item.py +++ /dev/null @@ -1,54 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ExposureMetaRecentPreviewTeaserItemsItem(UniversalBaseModel): - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL."), - ] - """ - Video thumbnail URL. - """ - - channel_name: typing_extensions.Annotated[ - str, FieldMetadata(alias="channelName"), pydantic.Field(alias="channelName", description="Source channel name.") - ] - """ - Source channel name. - """ - - published_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish date of the withheld video."), - ] - """ - Publish date of the withheld video. - """ - - duration: typing.Optional[str] = pydantic.Field(default=None) - """ - Video length as display text. Null when unknown. - """ - - title: str = pydantic.Field() - """ - Video title, truncated server side. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_totals.py b/src/arcmira/types/exposure_meta_totals.py deleted file mode 100644 index a20a40d..0000000 --- a/src/arcmira/types/exposure_meta_totals.py +++ /dev/null @@ -1,71 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ExposureMetaTotals(UniversalBaseModel): - """ - True totals behind the returned rows. Each response sets only the keys for its own sections. - """ - - appearances: typing.Optional[int] = pydantic.Field(default=None) - """ - Total media rows: appearances for a person, mentions for other types, episodes for a channel. - """ - - mentions: typing.Optional[int] = pydantic.Field(default=None) - """ - Person pages only: total media that mention the person. - """ - - topics: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related topics. - """ - - people: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related people. - """ - - brands: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related organizations, under the legacy key brands. - """ - - products: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related products. - """ - - channels: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related channels. - """ - - organizations: typing.Optional[int] = pydantic.Field(default=None) - """ - Total related organizations (organizations list). - """ - - guests: typing.Optional[int] = pydantic.Field(default=None) - """ - Channel only: total unique guests. - """ - - hosts: typing.Optional[int] = pydantic.Field(default=None) - """ - Channel only: total hosts. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/exposure_meta_usage_limit_type.py b/src/arcmira/types/exposure_meta_usage_limit_type.py deleted file mode 100644 index d5388cd..0000000 --- a/src/arcmira/types/exposure_meta_usage_limit_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ExposureMetaUsageLimitType = typing.Union[typing.Literal["lifetime", "monthly"], typing.Any] diff --git a/src/arcmira/types/feedback_correction_result.py b/src/arcmira/types/feedback_correction_result.py index 305ad75..b07223a 100644 --- a/src/arcmira/types/feedback_correction_result.py +++ b/src/arcmira/types/feedback_correction_result.py @@ -4,7 +4,6 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .feedback_correction_result_recommendation import FeedbackCorrectionResultRecommendation from .feedback_correction_result_status import FeedbackCorrectionResultStatus @@ -24,21 +23,6 @@ class FeedbackCorrectionResult(UniversalBaseModel): Outcome. Values: applied (the correction was applied automatically), unchanged (the target already had the requested value), not_found (the target does not exist), invalid (the correction payload was malformed for its kind), logged (recorded for human review; no automatic apply). """ - previous_mention_class: typing.Optional[str] = pydantic.Field(default=None) - """ - The mention_class before the correction. Only present for recommendation-class corrections. - """ - - new_mention_class: typing.Optional[str] = pydantic.Field(default=None) - """ - The mention_class requested. Only present for recommendation-class corrections. - """ - - rows_affected: typing.Optional[int] = pydantic.Field(default=None) - """ - Rows updated by an applied correction. - """ - reason: typing.Optional[str] = pydantic.Field(default=None) """ The reason code you supplied, echoed back. @@ -49,11 +33,6 @@ class FeedbackCorrectionResult(UniversalBaseModel): Human-readable explanation of the outcome. """ - recommendation: typing.Optional[FeedbackCorrectionResultRecommendation] = pydantic.Field(default=None) - """ - The recommendation after the correction. Only present for recommendation-class corrections that resolved a row. - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/arcmira/types/feedback_correction_result_recommendation.py b/src/arcmira/types/feedback_correction_result_recommendation.py deleted file mode 100644 index 55f668d..0000000 --- a/src/arcmira/types/feedback_correction_result_recommendation.py +++ /dev/null @@ -1,105 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_ref import EntityRef -from .feedback_correction_result_recommendation_media import FeedbackCorrectionResultRecommendationMedia - - -class FeedbackCorrectionResultRecommendation(UniversalBaseModel): - """ - The recommendation after the correction. Only present for recommendation-class corrections that resolved a row. - """ - - id: str = pydantic.Field() - """ - Public recommendation id in the form "com_{n}". - """ - - recommendation_id: int = pydantic.Field() - """ - Raw integer id of the recommendation row. Same number as in the "com_{n}" public id. - """ - - mention_class: str = pydantic.Field() - """ - Commercial mention classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention). - """ - - entity: EntityRef - media: FeedbackCorrectionResultRecommendationMedia - start_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds. - """ - - end_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds. - """ - - start_seconds: typing.Optional[int] = pydantic.Field(default=None) - """ - Start position in the video in integer SECONDS, parsed from start_timestamp. Prefer this over the deprecated string field. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. - """ - - end_seconds: typing.Optional[int] = pydantic.Field(default=None) - """ - End position in the video in integer SECONDS, parsed from end_timestamp. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. - """ - - verbatim_quote: typing.Optional[str] = pydantic.Field(default=None) - """ - Verbatim quote from the transcript. Null when no quote was extracted. - """ - - promo_code: typing.Optional[str] = pydantic.Field(default=None) - """ - Promo code read out in the mention. Null unless one was detected. - """ - - offer: typing.Optional[str] = pydantic.Field(default=None) - """ - Offer text, e.g. "20% off your first order". Null unless one was detected. - """ - - sentiment: typing.Optional[float] = pydantic.Field(default=None) - """ - DEPRECATED: use sentiment_score, which carries the same number. Removal will be announced in the changelog. Raw NUMERIC sentiment score between -1 and 1. Null when not computed. - """ - - sentiment_score: typing.Optional[float] = pydantic.Field(default=None) - """ - Raw sentiment score between -1 and 1, same semantics as sentiment_score on mention rows. Null when not computed. - """ - - confidence: float = pydantic.Field() - """ - Classifier confidence between 0 and 1. Rows below the min_confidence filter (default 0.7) are excluded from list responses. - """ - - speaker_role: str = pydantic.Field() - """ - Role of the speaker delivering the mention, e.g. "host" or "guest". - """ - - conflict_status: typing.Optional[str] = pydantic.Field(default=None) - """ - Set when community feedback disputes the classification (e.g. "disputed"). Null when undisputed. Disputed rows are excluded unless include_disputed=true. - """ - - resolution: typing.Optional[str] = pydantic.Field(default=None) - """ - How a disputed classification was resolved. Null until a dispute has been resolved. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/feedback_correction_result_recommendation_media.py b/src/arcmira/types/feedback_correction_result_recommendation_media.py deleted file mode 100644 index d8b1b9e..0000000 --- a/src/arcmira/types/feedback_correction_result_recommendation_media.py +++ /dev/null @@ -1,47 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .feedback_correction_result_recommendation_media_source_channel import ( - FeedbackCorrectionResultRecommendationMediaSourceChannel, -) - - -class FeedbackCorrectionResultRecommendationMedia(UniversalBaseModel): - video_id: str = pydantic.Field() - """ - YouTube video id (11 characters). - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - published_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Video publish timestamp. - """ - - channel_id: typing.Optional[str] = pydantic.Field(default=None) - """ - YouTube channel id of the source channel. - """ - - source_channel: typing.Optional[FeedbackCorrectionResultRecommendationMediaSourceChannel] = pydantic.Field( - default=None - ) - """ - The channel entity that published the video. Null when the video has not been linked to a channel entity. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/feedback_readback_correction.py b/src/arcmira/types/feedback_readback_correction.py index 090e43b..12389c6 100644 --- a/src/arcmira/types/feedback_readback_correction.py +++ b/src/arcmira/types/feedback_readback_correction.py @@ -20,7 +20,7 @@ class FeedbackReadbackCorrection(UniversalBaseModel): issue_type: typing.Optional[str] = pydantic.Field(default=None) """ - The issue_type as submitted. Null when the correction carried only a reason or mention_class. + The issue_type as submitted. Null when the correction carried only a reason or class. """ reason: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/arcmira/types/feedback_readback_response.py b/src/arcmira/types/feedback_readback_response.py index 767d94e..62de539 100644 --- a/src/arcmira/types/feedback_readback_response.py +++ b/src/arcmira/types/feedback_readback_response.py @@ -9,14 +9,14 @@ class FeedbackReadbackResponse(UniversalBaseModel): - feedback_id: int = pydantic.Field() + feedback_id: str = pydantic.Field() """ - Id of the feedback record. + Id of the feedback record, fbk_ and digits. """ type: str = pydantic.Field() """ - The feedback type as submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search. + The feedback type as submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search, experience. """ status: FeedbackReadbackResponseStatus = pydantic.Field() diff --git a/src/arcmira/types/feedback_response.py b/src/arcmira/types/feedback_response.py index 3d39ea7..ca82be4 100644 --- a/src/arcmira/types/feedback_response.py +++ b/src/arcmira/types/feedback_response.py @@ -8,19 +8,19 @@ class FeedbackResponse(UniversalBaseModel): - feedback_id: int = pydantic.Field() + feedback_id: str = pydantic.Field() """ - Id of the persisted feedback record. Read it back via GET /v1/feedback/{feedback_id}. + Id of the persisted feedback record, fbk_ and digits. Read it back via GET /v1/feedback/{feedback_id}. """ type: str = pydantic.Field() """ - The feedback type you submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search. + The feedback type you submitted. Values: recommendations, channel_sponsors, mentions, entities_search, entities, channels, monitor_alert, appearances, search, experience. """ query: typing.Dict[str, typing.Any] = pydantic.Field() """ - The query object the feedback is attached to, echoed back. + The query object the feedback is attached to, echoed back. category and mcp_call_id, when sent, are recorded in it under those names. """ applied: int = pydantic.Field() diff --git a/src/arcmira/types/me_response.py b/src/arcmira/types/me_response.py index 588b57c..2fa101a 100644 --- a/src/arcmira/types/me_response.py +++ b/src/arcmira/types/me_response.py @@ -42,7 +42,7 @@ class MeResponse(UniversalBaseModel): tier: str = pydantic.Field() """ - Plan tier, e.g. free, hobby, pro, teams, enterprise. + Plan tier, e.g. free, hobby, pro, enterprise. """ scopes: typing.List[str] = pydantic.Field() @@ -52,7 +52,7 @@ class MeResponse(UniversalBaseModel): rate_limit: int = pydantic.Field() """ - Requests allowed per 60-second window for this key: 600 for enterprise/teams, 240 for other paid tiers, 60 for free, unless a per-key override is set. + Requests allowed per 60-second window for this key: 600 for enterprise, 240 for other paid tiers, 60 for free, unless a per-key override is set. """ recommendations_api_enabled: bool = pydantic.Field() diff --git a/src/arcmira/types/mention.py b/src/arcmira/types/mention.py index 4687cf8..a5df6a8 100644 --- a/src/arcmira/types/mention.py +++ b/src/arcmira/types/mention.py @@ -16,31 +16,16 @@ class Mention(UniversalBaseModel): Public mention id in the form "men_{n}". """ - appearance_id: int = pydantic.Field() - """ - Raw integer id of the underlying appearance row. Same number as in the "men_{n}" public id. - """ - entity: EntityRef media: MentionMedia - start_timestamp: typing.Optional[str] = pydantic.Field(default=None) - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds. Null when the analyzer could not locate the mention in time. - """ - - end_timestamp: typing.Optional[str] = pydantic.Field(default=None) - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds. Null when unknown. - """ - start_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - Start position in the video in integer SECONDS, parsed from start_timestamp. Prefer this over the deprecated string field. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + Start position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ end_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - End position in the video in integer SECONDS, parsed from end_timestamp. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + End position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ is_appearance: bool = pydantic.Field() diff --git a/src/arcmira/types/mention_counts_response.py b/src/arcmira/types/mention_counts_response.py index 64dfc9f..a8d2700 100644 --- a/src/arcmira/types/mention_counts_response.py +++ b/src/arcmira/types/mention_counts_response.py @@ -3,12 +3,11 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata from .mention_counts_response_mode import MentionCountsResponseMode from .mention_counts_response_rows_item import MentionCountsResponseRowsItem from .mention_counts_response_shared_item import MentionCountsResponseSharedItem +from .publication_window import PublicationWindow class MentionCountsResponse(UniversalBaseModel): @@ -17,26 +16,13 @@ class MentionCountsResponse(UniversalBaseModel): The mode applied. """ - published_after: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="publishedAfter"), pydantic.Field(alias="publishedAfter") - ] = None - published_before: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="publishedBefore"), pydantic.Field(alias="publishedBefore") - ] = None - channel_ids: typing_extensions.Annotated[ - typing.List[str], - FieldMetadata(alias="channelIds"), - pydantic.Field(alias="channelIds", description="The channel ids counted."), - ] + window: PublicationWindow + channel_ids: typing.List[str] = pydantic.Field() """ The channel ids counted. """ - video_ids: typing_extensions.Annotated[ - typing.List[str], - FieldMetadata(alias="videoIds"), - pydantic.Field(alias="videoIds", description="The video ids the count was scoped to. Empty when it was not."), - ] + video_ids: typing.List[str] = pydantic.Field() """ The video ids the count was scoped to. Empty when it was not. """ diff --git a/src/arcmira/types/mention_list_response.py b/src/arcmira/types/mention_list_response.py index a41d785..d3ee6fc 100644 --- a/src/arcmira/types/mention_list_response.py +++ b/src/arcmira/types/mention_list_response.py @@ -4,13 +4,18 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .entity import Entity from .mention import Mention -from .mention_list_response_entity import MentionListResponseEntity from .mention_list_response_unlock import MentionListResponseUnlock +from .publication_window import PublicationWindow class MentionListResponse(UniversalBaseModel): - data: typing.List[Mention] + mentions: typing.List[Mention] = pydantic.Field() + """ + Newest first. + """ + has_more: bool = pydantic.Field() """ True when more rows exist past this page. @@ -21,11 +26,8 @@ class MentionListResponse(UniversalBaseModel): Opaque cursor for the next page. Null on the last page. """ - entity: MentionListResponseEntity = pydantic.Field() - """ - The resolved entity the mentions belong to. - """ - + entity: Entity + window: PublicationWindow note: typing.Optional[str] = pydantic.Field(default=None) """ Present on a free preview page: where the list stops and the plan that lifts it. Say so rather than calling this every mention. diff --git a/src/arcmira/types/mention_list_response_entity.py b/src/arcmira/types/mention_list_response_entity.py deleted file mode 100644 index fa10ce0..0000000 --- a/src/arcmira/types/mention_list_response_entity.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class MentionListResponseEntity(UniversalBaseModel): - """ - The resolved entity the mentions belong to. - """ - - id: str = pydantic.Field() - """ - Public entity id in the form "ent_{n}". Always the canonical entity id. - """ - - numeric_id: int = pydantic.Field() - """ - Raw integer database id of the canonical entity. Prefer the public "ent_{n}" id in requests. - """ - - canonical_id: str = pydantic.Field() - """ - Public id of the canonical entity. Identical to id. - """ - - name: str = pydantic.Field() - """ - Canonical entity name. - """ - - type: str = pydantic.Field() - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - platform: typing.Optional[str] = pydantic.Field(default=None) - """ - Source platform for channel entities, e.g. "youtube". Null unless the entity is platform-bound. - """ - - url: typing.Optional[str] = pydantic.Field(default=None) - """ - Canonical external URL for the entity. Null when none is known. - """ - - image_url: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity image URL. Null until an image has been resolved. - """ - - image_checked_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Timestamp of the last image resolution attempt. Null until the image pipeline has visited this entity. - """ - - appearance_count: typing.Optional[int] = pydantic.Field(default=None) - """ - Number of indexed appearance/mention rows for this entity. 0 when never counted. - """ - - owner_entity_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Public id ("ent_{n}") of the owning entity, e.g. the organization behind a product. Null unless an ownership link exists. - """ - - is_canonical: bool = pydantic.Field() - """ - True when the id you supplied is the canonical entity. False when your id was merged into this canonical record. - """ - - merged_from_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Public id you supplied when it differs from the canonical entity, i.e. your id was merged into this record. Null unless a merge redirect happened. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - URL slug, the site's canonical id for every type but channel. Null when never slugged. - """ - - route: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative route of this entity's page on arcmira.com, e.g. "/org/ramp", "/person/jane-doe", "/yt/@TBPNLive". Null for a type the site has no page for. - """ - - page: typing.Optional[str] = pydantic.Field(default=None) - """ - The entity's page on arcmira.com, absolute. Link the name to it when you write the entity into an answer. Null for a type the site has no page for. - """ - - appearances_page: typing.Optional[str] = pydantic.Field(default=None) - """ - For a person, the list of their appearances on arcmira.com (their page opens on it). Null for every other type. - """ - - mentions_page: typing.Optional[str] = pydantic.Field(default=None) - """ - For a person, the list of mentions of them on arcmira.com, page/mentions. Null for every other type: their page is the mentions list already. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/mention_media.py b/src/arcmira/types/mention_media.py index 5a443db..51ac8fd 100644 --- a/src/arcmira/types/mention_media.py +++ b/src/arcmira/types/mention_media.py @@ -8,11 +8,6 @@ class MentionMedia(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer media row id. - """ - video_id: str = pydantic.Field() """ YouTube video id (11 characters). diff --git a/src/arcmira/types/mention_recommendations.py b/src/arcmira/types/mention_recommendations.py index 4801c07..c5f4079 100644 --- a/src/arcmira/types/mention_recommendations.py +++ b/src/arcmira/types/mention_recommendations.py @@ -14,7 +14,7 @@ class MentionRecommendations(UniversalBaseModel): items: typing.List[RecommendationEnrichmentItem] = pydantic.Field() """ - Commercial mentions (ad reads, endorsements) for the same entity in the same video. + Commercial mentions (sponsored and organic) for the same entity in the same video. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/merge_suggestion_change.py b/src/arcmira/types/merge_suggestion_change.py index e99f192..ed706de 100644 --- a/src/arcmira/types/merge_suggestion_change.py +++ b/src/arcmira/types/merge_suggestion_change.py @@ -3,9 +3,7 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class MergeSuggestionChange(UniversalBaseModel): @@ -13,47 +11,27 @@ class MergeSuggestionChange(UniversalBaseModel): For issue_type merge_suggestion (and duplicate_entity): the canonical merge you are proposing. Provide ids when you have them, names otherwise. """ - source_entity_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="sourceEntityId"), - pydantic.Field( - alias="sourceEntityId", description='Public id ("ent_{n}") of the duplicate/variant entity to merge away.' - ), - ] = None + source_entity_id: typing.Optional[str] = pydantic.Field(default=None) """ Public id ("ent_{n}") of the duplicate/variant entity to merge away. """ - target_entity_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="targetEntityId"), - pydantic.Field( - alias="targetEntityId", description='Public id ("ent_{n}") of the canonical entity to merge into.' - ), - ] = None + target_entity_id: typing.Optional[str] = pydantic.Field(default=None) """ Public id ("ent_{n}") of the canonical entity to merge into. """ - source_name: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="sourceName"), - pydantic.Field(alias="sourceName", description="Name of the duplicate entity when you do not have its id."), - ] = None + source_name: typing.Optional[str] = pydantic.Field(default=None) """ Name of the duplicate entity when you do not have its id. """ merge_into: typing.Optional[str] = pydantic.Field(default=None) """ - Name or public id of the canonical entity when you do not have targetEntityId. + Name or public id of the canonical entity when you do not have target_entity_id. """ - scope_type: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="scopeType"), - pydantic.Field(alias="scopeType", description='Scope of the merge rule, e.g. "global".'), - ] = None + scope_type: typing.Optional[str] = pydantic.Field(default=None) """ Scope of the merge rule, e.g. "global". """ diff --git a/src/arcmira/types/monitor.py b/src/arcmira/types/monitor.py index 3ecfe60..54dd865 100644 --- a/src/arcmira/types/monitor.py +++ b/src/arcmira/types/monitor.py @@ -3,10 +3,10 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from .monitor_access import MonitorAccess from .monitor_email_recipients_item import MonitorEmailRecipientsItem +from .monitor_team import MonitorTeam class Monitor(UniversalBaseModel): @@ -20,216 +20,111 @@ class Monitor(UniversalBaseModel): Monitor name. """ - is_collapsed: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isCollapsed"), - pydantic.Field(alias="isCollapsed", description="True when the monitor is collapsed in the dashboard UI."), - ] - """ - True when the monitor is collapsed in the dashboard UI. - """ - - is_paused: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPaused"), - pydantic.Field( - alias="isPaused", - description="True when delivery is paused for all trackers in this monitor. New alerts are not queued, and queued email delivery checks the pause state again before sending. Use PATCH /v1/trackers/{id} with paused: true to pause one tracker.", - ), - ] + paused: bool = pydantic.Field() """ True when delivery is paused for all trackers in this monitor. New alerts are not queued, and queued email delivery checks the pause state again before sending. Use PATCH /v1/trackers/{id} with paused: true to pause one tracker. """ - sort_order: typing_extensions.Annotated[ - int, FieldMetadata(alias="sortOrder"), pydantic.Field(alias="sortOrder", description="Dashboard sort position.") - ] - """ - Dashboard sort position. - """ - - notify_emails: typing_extensions.Annotated[ - typing.List[str], - FieldMetadata(alias="notifyEmails"), - pydantic.Field( - alias="notifyEmails", - description="Configured email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor.", - ), - ] + notify_emails: typing.List[str] = pydantic.Field() """ Configured email recipients. External recipients must confirm before delivery. Free includes one additional recipient per monitor. """ - email_recipients: typing_extensions.Annotated[ - typing.Optional[typing.List[MonitorEmailRecipientsItem]], - FieldMetadata(alias="emailRecipients"), - pydantic.Field( - alias="emailRecipients", - description="Recipient consent and invitation state. An account is not required to accept.", - ), - ] = None + email_recipients: typing.Optional[typing.List[MonitorEmailRecipientsItem]] = pydantic.Field(default=None) """ - Recipient consent and invitation state. An account is not required to accept. + Every address the monitor reaches, with consent and invitation state. An account is not required to accept. """ - notify_frequency: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="notifyFrequency"), - pydantic.Field( - alias="notifyFrequency", - description="Delivery cadence. Values: realtime (deliver immediately), hourly (hourly digest), daily (daily digest). Free tier is limited to daily.", - ), - ] = None + notify_frequency: typing.Optional[str] = pydantic.Field(default=None) """ Delivery cadence. Values: realtime (deliver immediately), hourly (hourly digest), daily (daily digest). Free tier is limited to daily. """ - digest_day: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="digestDay"), - pydantic.Field(alias="digestDay", description="Day of week for digest delivery."), - ] = None + digest_day: typing.Optional[str] = pydantic.Field(default=None) """ Day of week for digest delivery. """ - digest_time: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="digestTime"), - pydantic.Field(alias="digestTime", description="Time of day (HH:MM) for digest delivery."), - ] = None + digest_time: typing.Optional[str] = pydantic.Field(default=None) """ Time of day (HH:MM) for digest delivery. """ - notify_webhook: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="notifyWebhook"), - pydantic.Field(alias="notifyWebhook", description="True when webhook delivery is enabled."), - ] + notify_webhook: bool = pydantic.Field() """ True when webhook delivery is enabled. """ - webhook_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookUrl"), - pydantic.Field(alias="webhookUrl", description="Webhook destination URL. Null when no webhook is configured."), - ] = None + webhook_url: typing.Optional[str] = pydantic.Field(default=None) """ - Webhook destination URL. Null when no webhook is configured. + Webhook destination URL. Null when no webhook is configured. Absent when access is member: only the team owner sees the webhook. """ - webhook_secret_set: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="webhookSecretSet"), - pydantic.Field( - alias="webhookSecretSet", - description="True when a webhook signing secret exists for this monitor. The secret itself is never returned on reads; enablement and rotation responses support recovery with the original Idempotency-Key during the valid recovery window.", - ), - ] + webhook_secret_set: typing.Optional[bool] = pydantic.Field(default=None) """ - True when a webhook signing secret exists for this monitor. The secret itself is never returned on reads; enablement and rotation responses support recovery with the original Idempotency-Key during the valid recovery window. + True when a webhook signing secret exists for this monitor. The secret itself is never returned on reads; enablement and rotation responses support recovery with the original Idempotency-Key during the valid recovery window. Absent when access is member. """ - webhook_secret_hint: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookSecretHint"), - pydantic.Field( - alias="webhookSecretHint", - description="Last 4 characters of the current signing secret, for identifying which secret you hold. Null until a secret exists.", - ), - ] = None + webhook_secret_hint: typing.Optional[str] = pydantic.Field(default=None) """ - Last 4 characters of the current signing secret, for identifying which secret you hold. Null until a secret exists. + Last 4 characters of the current signing secret, for identifying which secret you hold. Null until a secret exists. Absent when access is member. """ - webhook_failures: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="webhookFailures"), - pydantic.Field( - alias="webhookFailures", - description="Consecutive webhook delivery failures recorded for this monitor. Reset by a secret rotation or PATCHing notifyWebhook: true; 10 consecutive failures auto-disable a webhook. Note: the delivery pipeline currently accrues failures on the tracker that fired, so this monitor-level counter can lag.", - ), - ] = None + webhook_failures: typing.Optional[int] = pydantic.Field(default=None) """ - Consecutive webhook delivery failures recorded for this monitor. Reset by a secret rotation or PATCHing notifyWebhook: true; 10 consecutive failures auto-disable a webhook. Note: the delivery pipeline currently accrues failures on the tracker that fired, so this monitor-level counter can lag. + Consecutive webhook delivery failures recorded for this monitor. Reset by a secret rotation or PATCHing notify_webhook: true; 10 consecutive failures auto-disable a webhook. Note: the delivery pipeline currently accrues failures on the tracker that fired, so this monitor-level counter can lag. """ - webhook_disabled_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookDisabledAt"), - pydantic.Field( - alias="webhookDisabledAt", - description="When the webhook was auto-disabled after repeated failures. Null while delivery is enabled. Re-enable by PATCHing notifyWebhook: true; rotation alone never re-enables.", - ), - ] = None + webhook_disabled_at: typing.Optional[str] = pydantic.Field(default=None) """ - When the webhook was auto-disabled after repeated failures. Null while delivery is enabled. Re-enable by PATCHing notifyWebhook: true; rotation alone never re-enables. + When the webhook was auto-disabled after repeated failures. Null while delivery is enabled. Re-enable by PATCHing notify_webhook: true; rotation alone never re-enables. """ - webhook_disabled_reason: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookDisabledReason"), - pydantic.Field( - alias="webhookDisabledReason", - description="Why the webhook was auto-disabled. Null while delivery is enabled.", - ), - ] = None + webhook_disabled_reason: typing.Optional[str] = pydantic.Field(default=None) """ Why the webhook was auto-disabled. Null while delivery is enabled. """ - notify_slack: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="notifySlack"), - pydantic.Field(alias="notifySlack", description="True when Slack delivery is enabled."), - ] + notify_slack: bool = pydantic.Field() """ True when Slack delivery is enabled. """ - slack_integration_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="slackIntegrationId"), - pydantic.Field( - alias="slackIntegrationId", - description="Slack integration used for delivery. Null when Slack is not configured.", - ), - ] = None + slack_integration_id: typing.Optional[str] = pydantic.Field(default=None) """ Slack integration used for delivery. Null when Slack is not configured. """ - slack_channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="slackChannelId"), - pydantic.Field( - alias="slackChannelId", description="Slack channel to deliver to. Null when Slack is not configured." - ), - ] = None + slack_channel_id: typing.Optional[str] = pydantic.Field(default=None) """ Slack channel to deliver to. Null when Slack is not configured. """ - created_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="createdAt"), - pydantic.Field(alias="createdAt", description="When the monitor was created."), - ] + created_at: str = pydantic.Field() """ When the monitor was created. """ - updated_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="updatedAt"), - pydantic.Field(alias="updatedAt", description="When the monitor was last updated."), - ] + updated_at: str = pydantic.Field() """ When the monitor was last updated. """ + team: typing.Optional[MonitorTeam] = pydantic.Field(default=None) + """ + The team the monitor is shared with. Null for a personal monitor. + """ + + access: MonitorAccess = pydantic.Field() + """ + account: the caller pays for the monitor, as its personal owner or the team owner. member: the caller is another member of its team, who may edit it but not its webhook, and may not delete it. + """ + + muted: bool = pydantic.Field() + """ + True when the caller muted this team monitor for themselves. Always false on a personal monitor. + """ + if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/arcmira/entities/mentions/types/list_mentions_request_details.py b/src/arcmira/types/monitor_access.py similarity index 59% rename from src/arcmira/entities/mentions/types/list_mentions_request_details.py rename to src/arcmira/types/monitor_access.py index 1b4c9c1..e466843 100644 --- a/src/arcmira/entities/mentions/types/list_mentions_request_details.py +++ b/src/arcmira/types/monitor_access.py @@ -2,4 +2,4 @@ import typing -ListMentionsRequestDetails = typing.Union[typing.Literal["full"], typing.Any] +MonitorAccess = typing.Union[typing.Literal["account", "member"], typing.Any] diff --git a/src/arcmira/types/team_spend_response.py b/src/arcmira/types/monitor_add_entities_response.py similarity index 60% rename from src/arcmira/types/team_spend_response.py rename to src/arcmira/types/monitor_add_entities_response.py index 46473fa..e33ce22 100644 --- a/src/arcmira/types/team_spend_response.py +++ b/src/arcmira/types/monitor_add_entities_response.py @@ -4,18 +4,18 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .team_member_spend import TeamMemberSpend +from .monitor_entity_result import MonitorEntityResult -class TeamSpendResponse(UniversalBaseModel): - period_start: str = pydantic.Field() +class MonitorAddEntitiesResponse(UniversalBaseModel): + monitor_id: str = pydantic.Field() """ - First day of the current period (YYYY-MM-DD). + The monitor the entities were added to. """ - data: typing.List[TeamMemberSpend] = pydantic.Field() + results: typing.List[MonitorEntityResult] = pydantic.Field() """ - Per-member spend rows, earliest join first. + One result per distinct requested entity id, in request order. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/monitor_add_trackers_response.py b/src/arcmira/types/monitor_add_trackers_response.py index e4aab09..eb45cea 100644 --- a/src/arcmira/types/monitor_add_trackers_response.py +++ b/src/arcmira/types/monitor_add_trackers_response.py @@ -3,17 +3,11 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class MonitorAddTrackersResponse(UniversalBaseModel): - attached_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="attachedCount"), - pydantic.Field(alias="attachedCount", description="Number of unique requested trackers attached."), - ] + attached_count: int = pydantic.Field() """ Number of unique requested trackers attached. """ @@ -23,11 +17,7 @@ class MonitorAddTrackersResponse(UniversalBaseModel): Human-readable confirmation, e.g. "Added 3 tracker(s) to monitor". """ - monitor_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="monitorId"), - pydantic.Field(alias="monitorId", description="The monitor id from the request path."), - ] + monitor_id: str = pydantic.Field() """ The monitor id from the request path. """ diff --git a/src/arcmira/types/monitor_delete_response.py b/src/arcmira/types/monitor_delete_response.py index 6aba82c..e03f6ba 100644 --- a/src/arcmira/types/monitor_delete_response.py +++ b/src/arcmira/types/monitor_delete_response.py @@ -3,9 +3,7 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class MonitorDeleteResponse(UniversalBaseModel): @@ -14,13 +12,7 @@ class MonitorDeleteResponse(UniversalBaseModel): Human-readable confirmation. """ - trackers_deleted: typing_extensions.Annotated[ - int, - FieldMetadata(alias="trackersDeleted"), - pydantic.Field( - alias="trackersDeleted", description="Number of trackers that were deleted along with the monitor." - ), - ] + trackers_deleted: int = pydantic.Field() """ Number of trackers that were deleted along with the monitor. """ diff --git a/src/arcmira/types/monitor_email_recipients_item.py b/src/arcmira/types/monitor_email_recipients_item.py index 0fb503f..9bf5321 100644 --- a/src/arcmira/types/monitor_email_recipients_item.py +++ b/src/arcmira/types/monitor_email_recipients_item.py @@ -3,21 +3,30 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata from .monitor_email_recipients_item_invitation_status import MonitorEmailRecipientsItemInvitationStatus +from .monitor_email_recipients_item_role import MonitorEmailRecipientsItemRole from .monitor_email_recipients_item_status import MonitorEmailRecipientsItemStatus class MonitorEmailRecipientsItem(UniversalBaseModel): email: str - status: MonitorEmailRecipientsItemStatus - invitation_status: typing_extensions.Annotated[ - typing.Optional[MonitorEmailRecipientsItemInvitationStatus], - FieldMetadata(alias="invitationStatus"), - pydantic.Field(alias="invitationStatus"), - ] = None + role: MonitorEmailRecipientsItemRole = pydantic.Field() + """ + owner: the paying account. member: a member of the monitor's team, who receives its alerts without an invitation and does not count toward the recipient limits. external: anyone else, who must confirm first. + """ + + user_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The Arcmira user behind an owner or member address. Null for external recipients. + """ + + status: MonitorEmailRecipientsItemStatus = pydantic.Field() + """ + muted: a team member muted this monitor for themselves. + """ + + invitation_status: typing.Optional[MonitorEmailRecipientsItemInvitationStatus] = None if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/arcmira/types/monitor_email_recipients_item_role.py b/src/arcmira/types/monitor_email_recipients_item_role.py new file mode 100644 index 0000000..58e61af --- /dev/null +++ b/src/arcmira/types/monitor_email_recipients_item_role.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MonitorEmailRecipientsItemRole = typing.Union[typing.Literal["owner", "member", "external"], typing.Any] diff --git a/src/arcmira/types/monitor_email_recipients_item_status.py b/src/arcmira/types/monitor_email_recipients_item_status.py index 402f68b..aaf831b 100644 --- a/src/arcmira/types/monitor_email_recipients_item_status.py +++ b/src/arcmira/types/monitor_email_recipients_item_status.py @@ -3,6 +3,8 @@ import typing MonitorEmailRecipientsItemStatus = typing.Union[ - typing.Literal["active", "pending", "unsubscribed", "suppressed", "removed", "owner_unverified", "plan_limited"], + typing.Literal[ + "active", "pending", "unsubscribed", "suppressed", "removed", "owner_unverified", "plan_limited", "muted" + ], typing.Any, ] diff --git a/src/arcmira/types/monitor_entity_result.py b/src/arcmira/types/monitor_entity_result.py new file mode 100644 index 0000000..08486f3 --- /dev/null +++ b/src/arcmira/types/monitor_entity_result.py @@ -0,0 +1,53 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .monitor_entity_result_reason import MonitorEntityResultReason + + +class MonitorEntityResult(UniversalBaseModel): + entity_id: str = pydantic.Field() + """ + The entity id as requested. + """ + + canonical_entity_id: typing.Optional[str] = pydantic.Field(default=None) + """ + Present when entity_id was merged: the canonical entity the tracker follows. + """ + + tracker_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The tracker that follows the entity: an existing one when the account already tracked it, else the one this request created. Null when no tracker could be used (see reason). + """ + + created: bool = pydantic.Field() + """ + true when this request created the tracker. + """ + + attached: bool = pydantic.Field() + """ + true when the tracker is in this monitor after the request, including when it already was. + """ + + reason: typing.Optional[MonitorEntityResultReason] = pydantic.Field(default=None) + """ + Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants). + """ + + current_monitor_id: typing.Optional[str] = pydantic.Field(default=None) + """ + With reason tracked_in_another_monitor: the monitor the tracker is in. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/monitor_entity_result_reason.py b/src/arcmira/types/monitor_entity_result_reason.py new file mode 100644 index 0000000..e2db12e --- /dev/null +++ b/src/arcmira/types/monitor_entity_result_reason.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MonitorEntityResultReason = typing.Union[ + typing.Literal[ + "entity_not_found", "entity_type_not_trackable", "tracker_limit_reached", "tracked_in_another_monitor" + ], + typing.Any, +] diff --git a/src/arcmira/types/monitor_list_response.py b/src/arcmira/types/monitor_list_response.py index e8f104f..73c9155 100644 --- a/src/arcmira/types/monitor_list_response.py +++ b/src/arcmira/types/monitor_list_response.py @@ -10,7 +10,7 @@ class MonitorListResponse(UniversalBaseModel): monitors: typing.List[MonitorListResponseMonitorsItem] = pydantic.Field() """ - All monitors for the account, ordered by dashboard sort position, then name. + The account's personal monitors and the monitors of every team it belongs to, ordered by dashboard sort position, then name. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/monitor_list_response_monitors_item.py b/src/arcmira/types/monitor_list_response_monitors_item.py index ab01c0d..6656d68 100644 --- a/src/arcmira/types/monitor_list_response_monitors_item.py +++ b/src/arcmira/types/monitor_list_response_monitors_item.py @@ -3,43 +3,23 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from ..core.serialization import FieldMetadata from .monitor import Monitor from .monitor_list_response_monitors_item_slack_integration import MonitorListResponseMonitorsItemSlackIntegration class MonitorListResponseMonitorsItem(Monitor): - tracker_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="trackerCount"), - pydantic.Field(alias="trackerCount", description="Number of trackers in the monitor."), - ] + tracker_count: int = pydantic.Field() """ Number of trackers in the monitor. """ - alerts_this_month: typing_extensions.Annotated[ - int, - FieldMetadata(alias="alertsThisMonth"), - pydantic.Field( - alias="alertsThisMonth", - description="Alert deliveries written for this monitor since the start of the calendar month.", - ), - ] + alerts_this_month: int = pydantic.Field() """ Alert deliveries written for this monitor since the start of the calendar month. """ - slack_integration: typing_extensions.Annotated[ - typing.Optional[MonitorListResponseMonitorsItemSlackIntegration], - FieldMetadata(alias="slackIntegration"), - pydantic.Field( - alias="slackIntegration", - description="Display metadata for the connected Slack integration. Null/absent when Slack is not configured.", - ), - ] = None + slack_integration: typing.Optional[MonitorListResponseMonitorsItemSlackIntegration] = pydantic.Field(default=None) """ Display metadata for the connected Slack integration. Null/absent when Slack is not configured. """ diff --git a/src/arcmira/types/monitor_mutation_response_monitor.py b/src/arcmira/types/monitor_mutation_response_monitor.py index d977c21..00fa675 100644 --- a/src/arcmira/types/monitor_mutation_response_monitor.py +++ b/src/arcmira/types/monitor_mutation_response_monitor.py @@ -3,34 +3,19 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from ..core.serialization import FieldMetadata from .monitor import Monitor class MonitorMutationResponseMonitor(Monitor): - tracker_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="trackerCount"), - pydantic.Field( - alias="trackerCount", description="Number of trackers in the monitor. Always 0 in the create response." - ), - ] + tracker_count: int = pydantic.Field() """ Number of trackers in the monitor. Always 0 in the create response. """ - webhook_secret: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookSecret"), - pydantic.Field( - alias="webhookSecret", - description='The webhook signing secret ("whsec_..."). Only present when this request NEWLY enabled webhook signing: a create with notifyWebhook: true and a webhookUrl, or a PATCH that turns the webhook on (or sets a URL) where no secret existed before. Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again.', - ), - ] = None + webhook_secret: typing.Optional[str] = pydantic.Field(default=None) """ - The webhook signing secret ("whsec_..."). Only present when this request NEWLY enabled webhook signing: a create with notifyWebhook: true and a webhookUrl, or a PATCH that turns the webhook on (or sets a URL) where no secret existed before. Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again. + The webhook signing secret ("whsec_..."). Only present when this request NEWLY enabled webhook signing: a create with notify_webhook: true and a webhook_url, or a PATCH that turns the webhook on (or sets a URL) where no secret existed before. Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/team_members_response_team.py b/src/arcmira/types/monitor_team.py similarity index 82% rename from src/arcmira/types/team_members_response_team.py rename to src/arcmira/types/monitor_team.py index f467793..e20ac4b 100644 --- a/src/arcmira/types/team_members_response_team.py +++ b/src/arcmira/types/monitor_team.py @@ -6,7 +6,11 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class TeamMembersResponseTeam(UniversalBaseModel): +class MonitorTeam(UniversalBaseModel): + """ + The team the monitor is shared with. Null for a personal monitor. + """ + id: str = pydantic.Field() """ Team id. diff --git a/src/arcmira/types/monitor_trackers_response_trackers_item.py b/src/arcmira/types/monitor_trackers_response_trackers_item.py index 0ffca3d..747cd81 100644 --- a/src/arcmira/types/monitor_trackers_response_trackers_item.py +++ b/src/arcmira/types/monitor_trackers_response_trackers_item.py @@ -3,9 +3,7 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class MonitorTrackersResponseTrackersItem(UniversalBaseModel): @@ -14,83 +12,47 @@ class MonitorTrackersResponseTrackersItem(UniversalBaseModel): Tracker id. """ - entity_name: typing_extensions.Annotated[ - str, FieldMetadata(alias="entityName"), pydantic.Field(alias="entityName", description="Tracked entity name.") - ] + entity_name: str = pydantic.Field() """ Tracked entity name. """ - entity_type: typing_extensions.Annotated[ - str, FieldMetadata(alias="entityType"), pydantic.Field(alias="entityType", description="Tracked entity type.") - ] + entity_type: str = pydantic.Field() """ Tracked entity type. """ - display_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="displayName"), - pydantic.Field( - alias="displayName", description="User-facing display name. Falls back to entityName when not customized." - ), - ] + display_name: str = pydantic.Field() """ - User-facing display name. Falls back to entityName when not customized. + User-facing display name. Falls back to entity_name when not customized. """ - is_paused: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPaused"), - pydantic.Field(alias="isPaused", description="True when the tracker is paused."), - ] + paused: bool = pydantic.Field() """ True when the tracker is paused. """ - paused_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="pausedAt"), - pydantic.Field(alias="pausedAt", description="When the tracker was paused. Null unless paused."), - ] = None + paused_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker was paused. Null unless paused. """ - last_notified_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="lastNotifiedAt"), - pydantic.Field( - alias="lastNotifiedAt", description="When the tracker last produced an alert. Null until the first alert." - ), - ] = None + last_notified_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker last produced an alert. Null until the first alert. """ - created_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="createdAt"), - pydantic.Field(alias="createdAt", description="When the tracker was created."), - ] + created_at: str = pydantic.Field() """ When the tracker was created. """ - updated_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="updatedAt"), - pydantic.Field(alias="updatedAt", description="When the tracker was last updated."), - ] = None + updated_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker was last updated. """ - monitor_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="monitorId"), - pydantic.Field(alias="monitorId", description="The monitor id from the request path."), - ] + monitor_id: str = pydantic.Field() """ The monitor id from the request path. """ diff --git a/src/arcmira/types/organization_page_response.py b/src/arcmira/types/organization_page_response.py deleted file mode 100644 index 0858ff5..0000000 --- a/src/arcmira/types/organization_page_response.py +++ /dev/null @@ -1,89 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_page_mention import EntityPageMention -from .exposure_meta import ExposureMeta -from .organization_page_response_channels_item import OrganizationPageResponseChannelsItem -from .organization_page_response_entity import OrganizationPageResponseEntity -from .organization_page_response_mentions_by_month_item import OrganizationPageResponseMentionsByMonthItem -from .organization_page_response_people_item import OrganizationPageResponsePeopleItem -from .organization_page_response_products_item import OrganizationPageResponseProductsItem -from .organization_page_response_role_edge import OrganizationPageResponseRoleEdge -from .organization_page_response_stats import OrganizationPageResponseStats -from .organization_page_response_topics_item import OrganizationPageResponseTopicsItem - - -class OrganizationPageResponse(UniversalBaseModel): - entity: OrganizationPageResponseEntity = pydantic.Field() - """ - The organization and what it owns. - """ - - role_edge: typing_extensions.Annotated[ - typing.Optional[OrganizationPageResponseRoleEdge], - FieldMetadata(alias="roleEdge"), - pydantic.Field( - alias="roleEdge", description="The verified CEO of the organization. Null when none is verified." - ), - ] = None - """ - The verified CEO of the organization. Null when none is verified. - """ - - stats: OrganizationPageResponseStats = pydantic.Field() - """ - Headline numbers and section totals for the organization. - """ - - mentions_by_month: typing_extensions.Annotated[ - typing.List[OrganizationPageResponseMentionsByMonthItem], - FieldMetadata(alias="mentionsByMonth"), - pydantic.Field( - alias="mentionsByMonth", - description="Media mentioning the organization per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Media mentioning the organization per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - topics: typing.List[OrganizationPageResponseTopicsItem] = pydantic.Field() - """ - Co-occurring topics, highest count first. - """ - - people: typing.List[OrganizationPageResponsePeopleItem] = pydantic.Field() - """ - Co-occurring people, highest count first. - """ - - products: typing.List[OrganizationPageResponseProductsItem] = pydantic.Field() - """ - Products the organization owns that share media with it, most mentions first. - """ - - channels: typing.List[OrganizationPageResponseChannelsItem] = pydantic.Field() - """ - Channels that mention the organization, highest count first. - """ - - mentions: typing.List[EntityPageMention] = pydantic.Field() - """ - Newest media that mention the entity, one row per video, cut to the plan's media rows. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_channels_item.py b/src/arcmira/types/organization_page_response_channels_item.py deleted file mode 100644 index 337b3bc..0000000 --- a/src/arcmira/types/organization_page_response_channels_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .organization_page_response_channels_item_sentiment import OrganizationPageResponseChannelsItemSentiment - - -class OrganizationPageResponseChannelsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Channel name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media on this channel that mention the entity. Null when the plan hides counts. - """ - - sentiment: OrganizationPageResponseChannelsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_channels_item_sentiment.py b/src/arcmira/types/organization_page_response_channels_item_sentiment.py deleted file mode 100644 index 488c6f2..0000000 --- a/src/arcmira/types/organization_page_response_channels_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseChannelsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/organization_page_response_entity.py b/src/arcmira/types/organization_page_response_entity.py deleted file mode 100644 index ca024cd..0000000 --- a/src/arcmira/types/organization_page_response_entity.py +++ /dev/null @@ -1,99 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .organization_page_response_entity_owned_channels_item import OrganizationPageResponseEntityOwnedChannelsItem -from .organization_page_response_entity_owned_products_item import OrganizationPageResponseEntityOwnedProductsItem -from .organization_page_response_entity_type import OrganizationPageResponseEntityType - - -class OrganizationPageResponseEntity(UniversalBaseModel): - """ - The organization and what it owns. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Organization name. - """ - - type: OrganizationPageResponseEntityType = pydantic.Field() - """ - Always organization. - """ - - category: str = pydantic.Field() - """ - Stored type: organization, company, or brand. - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until resolved."), - ] = None - """ - Logo URL. Null until resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - is_priority: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPriority"), - pydantic.Field(alias="isPriority", description="True when the entity is flagged priority."), - ] - """ - True when the entity is flagged priority. - """ - - owned_channels: typing_extensions.Annotated[ - typing.Optional[typing.List[OrganizationPageResponseEntityOwnedChannelsItem]], - FieldMetadata(alias="ownedChannels"), - pydantic.Field( - alias="ownedChannels", - description="Up to 10 channels this entity owns, most videos first. Null when it owns none.", - ), - ] = None - """ - Up to 10 channels this entity owns, most videos first. Null when it owns none. - """ - - owned_products: typing_extensions.Annotated[ - typing.Optional[typing.List[OrganizationPageResponseEntityOwnedProductsItem]], - FieldMetadata(alias="ownedProducts"), - pydantic.Field( - alias="ownedProducts", - description="Up to 10 products this entity owns, most mentions first. Null when it owns none.", - ), - ] = None - """ - Up to 10 products this entity owns, most mentions first. Null when it owns none. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_entity_owned_channels_item.py b/src/arcmira/types/organization_page_response_entity_owned_channels_item.py deleted file mode 100644 index ca1fef9..0000000 --- a/src/arcmira/types/organization_page_response_entity_owned_channels_item.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseEntityOwnedChannelsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Entity name. - """ - - display_name: str = pydantic.Field() - """ - Label that tells a homonym apart, e.g. "AdQuick (channel)". Equals name when no label is needed. - """ - - type: str = pydantic.Field() - """ - Entity type: channel or product. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the entity page on arcmira.com. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Entity image URL. Null until resolved."), - ] = None - """ - Entity image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - video_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="videoCount"), - pydantic.Field(alias="videoCount", description="Channels only: media rows published by the channel."), - ] = None - """ - Channels only: media rows published by the channel. - """ - - mention_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="mentionCount"), - pydantic.Field(alias="mentionCount", description="Products only: appearance rows of the product."), - ] = None - """ - Products only: appearance rows of the product. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_entity_owned_products_item.py b/src/arcmira/types/organization_page_response_entity_owned_products_item.py deleted file mode 100644 index a523d2a..0000000 --- a/src/arcmira/types/organization_page_response_entity_owned_products_item.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseEntityOwnedProductsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Entity name. - """ - - display_name: str = pydantic.Field() - """ - Label that tells a homonym apart, e.g. "AdQuick (channel)". Equals name when no label is needed. - """ - - type: str = pydantic.Field() - """ - Entity type: channel or product. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the entity page on arcmira.com. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Entity image URL. Null until resolved."), - ] = None - """ - Entity image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - video_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="videoCount"), - pydantic.Field(alias="videoCount", description="Channels only: media rows published by the channel."), - ] = None - """ - Channels only: media rows published by the channel. - """ - - mention_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="mentionCount"), - pydantic.Field(alias="mentionCount", description="Products only: appearance rows of the product."), - ] = None - """ - Products only: appearance rows of the product. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_entity_type.py b/src/arcmira/types/organization_page_response_entity_type.py deleted file mode 100644 index 16a4bd9..0000000 --- a/src/arcmira/types/organization_page_response_entity_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseEntityType = typing.Union[typing.Literal["organization"], typing.Any] diff --git a/src/arcmira/types/organization_page_response_mentions_by_month_item.py b/src/arcmira/types/organization_page_response_mentions_by_month_item.py deleted file mode 100644 index fd2c490..0000000 --- a/src/arcmira/types/organization_page_response_mentions_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseMentionsByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_people_item.py b/src/arcmira/types/organization_page_response_people_item.py deleted file mode 100644 index 343907b..0000000 --- a/src/arcmira/types/organization_page_response_people_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .organization_page_response_people_item_sentiment import OrganizationPageResponsePeopleItemSentiment - - -class OrganizationPageResponsePeopleItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: OrganizationPageResponsePeopleItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_people_item_sentiment.py b/src/arcmira/types/organization_page_response_people_item_sentiment.py deleted file mode 100644 index e9e6494..0000000 --- a/src/arcmira/types/organization_page_response_people_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponsePeopleItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/organization_page_response_products_item.py b/src/arcmira/types/organization_page_response_products_item.py deleted file mode 100644 index 06c11a5..0000000 --- a/src/arcmira/types/organization_page_response_products_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .organization_page_response_products_item_sentiment import OrganizationPageResponseProductsItemSentiment - - -class OrganizationPageResponseProductsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: OrganizationPageResponseProductsItemSentiment = pydantic.Field() - """ - Always neutral. - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_products_item_sentiment.py b/src/arcmira/types/organization_page_response_products_item_sentiment.py deleted file mode 100644 index b532aa1..0000000 --- a/src/arcmira/types/organization_page_response_products_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseProductsItemSentiment = typing.Union[typing.Literal["neutral"], typing.Any] diff --git a/src/arcmira/types/organization_page_response_role_edge.py b/src/arcmira/types/organization_page_response_role_edge.py deleted file mode 100644 index 95b1608..0000000 --- a/src/arcmira/types/organization_page_response_role_edge.py +++ /dev/null @@ -1,137 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .organization_page_response_role_edge_label import OrganizationPageResponseRoleEdgeLabel -from .organization_page_response_role_edge_people_item import OrganizationPageResponseRoleEdgePeopleItem -from .organization_page_response_role_edge_recent_appearances_item import ( - OrganizationPageResponseRoleEdgeRecentAppearancesItem, -) -from .organization_page_response_role_edge_role import OrganizationPageResponseRoleEdgeRole - - -class OrganizationPageResponseRoleEdge(UniversalBaseModel): - """ - The verified CEO of the organization. Null when none is verified. - """ - - role: OrganizationPageResponseRoleEdgeRole = pydantic.Field() - """ - Always ceo. - """ - - label: OrganizationPageResponseRoleEdgeLabel = pydantic.Field() - """ - CO-CEOS when operator-certified co-CEOs lead the organization, CEO otherwise. - """ - - name: str = pydantic.Field() - """ - CEO name, or the co-CEO names joined with "and". - """ - - href: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative link to the CEO page. Null for co-CEOs (see people) or when none. - """ - - receipt: typing.Optional[str] = pydantic.Field(default=None) - """ - Display line naming the evidence for the primary CEO. Null when there is none to show. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Primary CEO image URL. Null until resolved."), - ] = None - """ - Primary CEO image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", - description="When the image pipeline last checked the primary CEO. Null until checked.", - ), - ] = None - """ - When the image pipeline last checked the primary CEO. Null until checked. - """ - - people: typing.Optional[typing.List[OrganizationPageResponseRoleEdgePeopleItem]] = pydantic.Field(default=None) - """ - Co-CEOs only: one entry per co-CEO. Absent for a single CEO. - """ - - confirmations: int = pydantic.Field() - """ - Most confirmations of the role across the CEO edges. - """ - - first_confirmed_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="firstConfirmedAt"), - pydantic.Field( - alias="firstConfirmedAt", description="When the primary CEO role was first confirmed. Null when unknown." - ), - ] = None - """ - When the primary CEO role was first confirmed. Null when unknown. - """ - - last_confirmed_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="lastConfirmedAt"), - pydantic.Field( - alias="lastConfirmedAt", description="When the primary CEO role was last confirmed. Null when unknown." - ), - ] = None - """ - When the primary CEO role was last confirmed. Null when unknown. - """ - - verified_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="verifiedAt"), - pydantic.Field(alias="verifiedAt", description="When the primary CEO role was verified."), - ] - """ - When the primary CEO role was verified. - """ - - true_appearances: typing_extensions.Annotated[ - int, - FieldMetadata(alias="trueAppearances"), - pydantic.Field(alias="trueAppearances", description="Most media any of the CEOs appeared in."), - ] - """ - Most media any of the CEOs appeared in. - """ - - recent_appearances: typing_extensions.Annotated[ - typing.List[OrganizationPageResponseRoleEdgeRecentAppearancesItem], - FieldMetadata(alias="recentAppearances"), - pydantic.Field( - alias="recentAppearances", - description="Newest media the CEO appeared in, outside the withheld window. Empty when no CEO has enough appearances to list.", - ), - ] - """ - Newest media the CEO appeared in, outside the withheld window. Empty when no CEO has enough appearances to list. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_role_edge_label.py b/src/arcmira/types/organization_page_response_role_edge_label.py deleted file mode 100644 index 2bbb404..0000000 --- a/src/arcmira/types/organization_page_response_role_edge_label.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseRoleEdgeLabel = typing.Union[typing.Literal["CEO", "CO-CEOS"], typing.Any] diff --git a/src/arcmira/types/organization_page_response_role_edge_people_item.py b/src/arcmira/types/organization_page_response_role_edge_people_item.py deleted file mode 100644 index 24d022e..0000000 --- a/src/arcmira/types/organization_page_response_role_edge_people_item.py +++ /dev/null @@ -1,63 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseRoleEdgePeopleItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Co-CEO name. - """ - - href: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative link to the co-CEO page. Null when none. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Co-CEO image URL. Null until resolved."), - ] = None - """ - Co-CEO image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this person. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this person. Null until checked. - """ - - true_appearances: typing_extensions.Annotated[ - int, - FieldMetadata(alias="trueAppearances"), - pydantic.Field(alias="trueAppearances", description="Media the co-CEO appeared in."), - ] - """ - Media the co-CEO appeared in. - """ - - receipt: typing.Optional[str] = pydantic.Field(default=None) - """ - Display line naming the evidence. Null when there is none to show. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_role_edge_recent_appearances_item.py b/src/arcmira/types/organization_page_response_role_edge_recent_appearances_item.py deleted file mode 100644 index 38bab0e..0000000 --- a/src/arcmira/types/organization_page_response_role_edge_recent_appearances_item.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseRoleEdgeRecentAppearancesItem(UniversalBaseModel): - title: str = pydantic.Field() - """ - Video title. - """ - - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - video_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id. Null when unknown."), - ] = None - """ - YouTube video id. Null when unknown. - """ - - channel: typing.Optional[str] = pydantic.Field(default=None) - """ - Source channel name. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="YouTube handle of the source channel. Null when unknown."), - ] = None - """ - YouTube handle of the source channel. Null when unknown. - """ - - timestamp: typing.Optional[str] = pydantic.Field(default=None) - """ - Appearance start as MM:SS text. Null when unknown. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_role_edge_role.py b/src/arcmira/types/organization_page_response_role_edge_role.py deleted file mode 100644 index a158b85..0000000 --- a/src/arcmira/types/organization_page_response_role_edge_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseRoleEdgeRole = typing.Union[typing.Literal["ceo"], typing.Any] diff --git a/src/arcmira/types/organization_page_response_stats.py b/src/arcmira/types/organization_page_response_stats.py deleted file mode 100644 index 460e40b..0000000 --- a/src/arcmira/types/organization_page_response_stats.py +++ /dev/null @@ -1,84 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class OrganizationPageResponseStats(UniversalBaseModel): - """ - Headline numbers and section totals for the organization. - """ - - velocity: typing.Optional[int] = pydantic.Field(default=None) - """ - Mentions in the last 90 days. Null when the plan hides it. - """ - - sentiment: float = pydantic.Field() - """ - Reserved. Always 0. - """ - - reach: str = pydantic.Field() - """ - Total views as display text, e.g. 1.2M. - """ - - reach_raw: typing_extensions.Annotated[ - float, - FieldMetadata(alias="reachRaw"), - pydantic.Field(alias="reachRaw", description="Total views of the media that mention the organization."), - ] - """ - Total views of the media that mention the organization. - """ - - total: int = pydantic.Field() - """ - Total media that mention the organization. - """ - - latest_media_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="latestMediaAt"), - pydantic.Field( - alias="latestMediaAt", - description="Newest publish date among the counted media. Null when none. The freshness gate does not withhold it.", - ), - ] = None - """ - Newest publish date among the counted media. Null when none. The freshness gate does not withhold it. - """ - - people: int = pydantic.Field() - """ - Total co-occurring people. - """ - - topics: int = pydantic.Field() - """ - Total co-occurring topics. - """ - - products: int = pydantic.Field() - """ - Owned products with mentions. - """ - - channels: int = pydantic.Field() - """ - Total channels that mention the organization. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_topics_item.py b/src/arcmira/types/organization_page_response_topics_item.py deleted file mode 100644 index 1c7574f..0000000 --- a/src/arcmira/types/organization_page_response_topics_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .organization_page_response_topics_item_sentiment import OrganizationPageResponseTopicsItemSentiment - - -class OrganizationPageResponseTopicsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: OrganizationPageResponseTopicsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/organization_page_response_topics_item_sentiment.py b/src/arcmira/types/organization_page_response_topics_item_sentiment.py deleted file mode 100644 index 4aaa2c5..0000000 --- a/src/arcmira/types/organization_page_response_topics_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -OrganizationPageResponseTopicsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/person_appearance_list_response.py b/src/arcmira/types/person_appearance_list_response.py deleted file mode 100644 index ca77cb7..0000000 --- a/src/arcmira/types/person_appearance_list_response.py +++ /dev/null @@ -1,48 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta import ExposureMeta -from .person_appearance_list_response_items_item import PersonAppearanceListResponseItemsItem - - -class PersonAppearanceListResponse(UniversalBaseModel): - items: typing.List[PersonAppearanceListResponseItemsItem] = pydantic.Field() - """ - Media the person appeared in, newest first by default, one row per video. - """ - - total: int = pydantic.Field() - """ - Rows matching the filter across all pages. - """ - - limit: int = pydantic.Field() - """ - Page size applied, after the plan clamp. - """ - - has_more: bool = pydantic.Field() - """ - True when more rows exist past this page. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_appearance_list_response_items_item.py b/src/arcmira/types/person_appearance_list_response_items_item.py deleted file mode 100644 index b8ca1bb..0000000 --- a/src/arcmira/types/person_appearance_list_response_items_item.py +++ /dev/null @@ -1,134 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_appearance_list_response_items_item_platform import PersonAppearanceListResponseItemsItemPlatform -from .person_appearance_list_response_items_item_sentiment import PersonAppearanceListResponseItemsItemSentiment -from .person_appearance_list_response_items_item_type import PersonAppearanceListResponseItemsItemType -from .published_excerpt import PublishedExcerpt - - -class PersonAppearanceListResponseItemsItem(UniversalBaseModel): - id: str = pydantic.Field() - """ - Raw appearance row id (the first row for the video), as a string. - """ - - date: str = pydantic.Field() - """ - Publish date as locale display text, or Unknown. - """ - - date_raw: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="dateRaw"), - pydantic.Field(alias="dateRaw", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - channel: str = pydantic.Field() - """ - Source channel name, or Unknown Channel. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id of the source channel. Null when unknown."), - ] = None - """ - YouTube channel id of the source channel. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="YouTube handle of the source channel. Null when unknown."), - ] = None - """ - YouTube handle of the source channel. Null when unknown. - """ - - platform: PersonAppearanceListResponseItemsItemPlatform = pydantic.Field() - """ - Always youtube. - """ - - type: PersonAppearanceListResponseItemsItemType = pydantic.Field() - """ - host when the person hosts the channel, guest otherwise. mention only when the request passed is_appearance=false, which lists media that mention the person instead. - """ - - context: str = pydantic.Field() - """ - Longest description of the appearance, or "No description available". - """ - - sentiment: PersonAppearanceListResponseItemsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - thumbnail: str = pydantic.Field() - """ - Video thumbnail URL. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - timestamp: str = pydantic.Field() - """ - Earliest start timestamp as MM:SS text, or "Full Episode" when none. - """ - - raw_timestamp: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="rawTimestamp"), - pydantic.Field(alias="rawTimestamp", description="Earliest start timestamp as stored. Null when none."), - ] = None - """ - Earliest start timestamp as stored. Null when none. - """ - - duration: typing.Optional[float] = pydantic.Field(default=None) - """ - Video length in seconds. Null when unknown. - """ - - view_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="viewCount"), - pydantic.Field(alias="viewCount", description="Video view count at index time. Null when never fetched."), - ] = None - """ - Video view count at index time. Null when never fetched. - """ - - excerpt: typing.Optional[PublishedExcerpt] = None - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_appearance_list_response_items_item_platform.py b/src/arcmira/types/person_appearance_list_response_items_item_platform.py deleted file mode 100644 index 5c740f8..0000000 --- a/src/arcmira/types/person_appearance_list_response_items_item_platform.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonAppearanceListResponseItemsItemPlatform = typing.Union[typing.Literal["youtube"], typing.Any] diff --git a/src/arcmira/types/person_appearance_list_response_items_item_sentiment.py b/src/arcmira/types/person_appearance_list_response_items_item_sentiment.py deleted file mode 100644 index 7d03959..0000000 --- a/src/arcmira/types/person_appearance_list_response_items_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonAppearanceListResponseItemsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/person_appearance_list_response_items_item_type.py b/src/arcmira/types/person_appearance_list_response_items_item_type.py deleted file mode 100644 index c1eb7cf..0000000 --- a/src/arcmira/types/person_appearance_list_response_items_item_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonAppearanceListResponseItemsItemType = typing.Union[typing.Literal["host", "guest", "mention"], typing.Any] diff --git a/src/arcmira/types/person_page_response.py b/src/arcmira/types/person_page_response.py deleted file mode 100644 index eb8c370..0000000 --- a/src/arcmira/types/person_page_response.py +++ /dev/null @@ -1,106 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .exposure_meta import ExposureMeta -from .person_page_response_appearances_by_month_item import PersonPageResponseAppearancesByMonthItem -from .person_page_response_appearances_item import PersonPageResponseAppearancesItem -from .person_page_response_brands_item import PersonPageResponseBrandsItem -from .person_page_response_entity import PersonPageResponseEntity -from .person_page_response_mentions_by_month_item import PersonPageResponseMentionsByMonthItem -from .person_page_response_mentions_item import PersonPageResponseMentionsItem -from .person_page_response_people_item import PersonPageResponsePeopleItem -from .person_page_response_products_item import PersonPageResponseProductsItem -from .person_page_response_role_edge import PersonPageResponseRoleEdge -from .person_page_response_stats import PersonPageResponseStats -from .person_page_response_topics_item import PersonPageResponseTopicsItem - - -class PersonPageResponse(UniversalBaseModel): - entity: PersonPageResponseEntity = pydantic.Field() - """ - The person: the stored entity row plus camelCase image fields and what the person owns. - """ - - role_edge: typing_extensions.Annotated[ - typing.Optional[PersonPageResponseRoleEdge], - FieldMetadata(alias="roleEdge"), - pydantic.Field(alias="roleEdge", description="A verified CEO role for the person. Null when none is verified."), - ] = None - """ - A verified CEO role for the person. Null when none is verified. - """ - - stats: PersonPageResponseStats = pydantic.Field() - """ - Headline numbers for the person. - """ - - appearances_by_month: typing_extensions.Annotated[ - typing.List[PersonPageResponseAppearancesByMonthItem], - FieldMetadata(alias="appearancesByMonth"), - pydantic.Field( - alias="appearancesByMonth", - description="Appearances per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Appearances per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - mentions_by_month: typing_extensions.Annotated[ - typing.List[PersonPageResponseMentionsByMonthItem], - FieldMetadata(alias="mentionsByMonth"), - pydantic.Field( - alias="mentionsByMonth", - description="Media mentioning the person per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Media mentioning the person per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - topics: typing.List[PersonPageResponseTopicsItem] = pydantic.Field() - """ - Topics in the media the person appeared in, highest count first. - """ - - people: typing.List[PersonPageResponsePeopleItem] = pydantic.Field() - """ - People in the media the person appeared in, highest count first. - """ - - brands: typing.List[PersonPageResponseBrandsItem] = pydantic.Field() - """ - Organizations in the media the person appeared in, highest count first. - """ - - products: typing.List[PersonPageResponseProductsItem] = pydantic.Field() - """ - Products in the media the person appeared in, highest count first. - """ - - appearances: typing.List[PersonPageResponseAppearancesItem] = pydantic.Field() - """ - Newest media the person appeared in, one row per video, cut to the plan's media rows. - """ - - mentions: typing.List[PersonPageResponseMentionsItem] = pydantic.Field() - """ - Newest media that mention the person without them appearing, one row per video. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_appearances_by_month_item.py b/src/arcmira/types/person_page_response_appearances_by_month_item.py deleted file mode 100644 index 025e6df..0000000 --- a/src/arcmira/types/person_page_response_appearances_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class PersonPageResponseAppearancesByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_appearances_item.py b/src/arcmira/types/person_page_response_appearances_item.py deleted file mode 100644 index ec82cad..0000000 --- a/src/arcmira/types/person_page_response_appearances_item.py +++ /dev/null @@ -1,134 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_appearances_item_platform import PersonPageResponseAppearancesItemPlatform -from .person_page_response_appearances_item_sentiment import PersonPageResponseAppearancesItemSentiment -from .person_page_response_appearances_item_type import PersonPageResponseAppearancesItemType -from .published_excerpt import PublishedExcerpt - - -class PersonPageResponseAppearancesItem(UniversalBaseModel): - id: str = pydantic.Field() - """ - Raw appearance row id (the first row for the video), as a string. - """ - - date: str = pydantic.Field() - """ - Publish date as locale display text, or Unknown. - """ - - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - channel: str = pydantic.Field() - """ - Source channel name, or Unknown Channel. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id of the source channel. Null when unknown."), - ] = None - """ - YouTube channel id of the source channel. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="YouTube handle of the source channel. Null when unknown."), - ] = None - """ - YouTube handle of the source channel. Null when unknown. - """ - - platform: PersonPageResponseAppearancesItemPlatform = pydantic.Field() - """ - Always youtube. - """ - - thumbnail: str = pydantic.Field() - """ - Video thumbnail URL. - """ - - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL. Same value as thumbnail."), - ] - """ - Video thumbnail URL. Same value as thumbnail. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - duration: typing.Optional[float] = pydantic.Field(default=None) - """ - Video length in seconds. Null when unknown. - """ - - type: PersonPageResponseAppearancesItemType = pydantic.Field() - """ - host or guest on an appearance row (host when the person hosts the channel); mention on a mention row. - """ - - context: str = pydantic.Field() - """ - Description of the moment, or "No description available". - """ - - sentiment: PersonPageResponseAppearancesItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - timestamp: str = pydantic.Field() - """ - Earliest start timestamp as MM:SS text, or "Full Episode" when none. - """ - - raw_timestamp: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="rawTimestamp"), - pydantic.Field(alias="rawTimestamp", description="Earliest start timestamp as stored. Null when none."), - ] = None - """ - Earliest start timestamp as stored. Null when none. - """ - - excerpt: typing.Optional[PublishedExcerpt] = None - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_appearances_item_platform.py b/src/arcmira/types/person_page_response_appearances_item_platform.py deleted file mode 100644 index c7fd173..0000000 --- a/src/arcmira/types/person_page_response_appearances_item_platform.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseAppearancesItemPlatform = typing.Union[typing.Literal["youtube"], typing.Any] diff --git a/src/arcmira/types/person_page_response_appearances_item_sentiment.py b/src/arcmira/types/person_page_response_appearances_item_sentiment.py deleted file mode 100644 index 9412cdb..0000000 --- a/src/arcmira/types/person_page_response_appearances_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseAppearancesItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/person_page_response_appearances_item_type.py b/src/arcmira/types/person_page_response_appearances_item_type.py deleted file mode 100644 index a5d58dc..0000000 --- a/src/arcmira/types/person_page_response_appearances_item_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseAppearancesItemType = typing.Union[typing.Literal["host", "guest", "mention"], typing.Any] diff --git a/src/arcmira/types/person_page_response_brands_item.py b/src/arcmira/types/person_page_response_brands_item.py deleted file mode 100644 index fb1fd36..0000000 --- a/src/arcmira/types/person_page_response_brands_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_brands_item_sentiment import PersonPageResponseBrandsItemSentiment - - -class PersonPageResponseBrandsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: PersonPageResponseBrandsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_brands_item_sentiment.py b/src/arcmira/types/person_page_response_brands_item_sentiment.py deleted file mode 100644 index 385d1d9..0000000 --- a/src/arcmira/types/person_page_response_brands_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseBrandsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/person_page_response_entity.py b/src/arcmira/types/person_page_response_entity.py deleted file mode 100644 index d12caa1..0000000 --- a/src/arcmira/types/person_page_response_entity.py +++ /dev/null @@ -1,132 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_entity_owned_channels_item import PersonPageResponseEntityOwnedChannelsItem -from .person_page_response_entity_owned_products_item import PersonPageResponseEntityOwnedProductsItem - - -class PersonPageResponseEntity(UniversalBaseModel): - """ - The person: the stored entity row plus camelCase image fields and what the person owns. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Person name. - """ - - type: str = pydantic.Field() - """ - Stored entity type. Always person. - """ - - platform: typing.Optional[str] = pydantic.Field(default=None) - """ - Source platform. Null for people. - """ - - url: typing.Optional[str] = pydantic.Field(default=None) - """ - External URL for the person. Null when none is known. - """ - - created_at: str = pydantic.Field() - """ - When the entity row was created. - """ - - image_url: typing.Optional[str] = pydantic.Field(default=None) - """ - Person image URL. Null until resolved. - """ - - image_checked_at: typing.Optional[str] = pydantic.Field(default=None) - """ - When the image pipeline last checked this entity. Null until checked. - """ - - merged_into_entity_id: typing.Optional[int] = pydantic.Field(default=None) - """ - Raw id of the entity this one was merged into. Null on a canonical entity, which a page always serves. - """ - - owner_entity_id: typing.Optional[int] = pydantic.Field(default=None) - """ - Raw id of the owning entity. Null unless an ownership link exists. - """ - - youtube_channel_id: typing.Optional[str] = pydantic.Field(default=None) - """ - YouTube channel id. Null for people. - """ - - youtube_handle: typing.Optional[str] = pydantic.Field(default=None) - """ - YouTube handle. Null for people. - """ - - is_priority: typing.Optional[int] = pydantic.Field(default=None) - """ - 1 when the entity is flagged priority, 0 or null otherwise. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Same value as image_url."), - ] = None - """ - Same value as image_url. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field(alias="imageCheckedAt", description="Same value as image_checked_at."), - ] = None - """ - Same value as image_checked_at. - """ - - owned_channels: typing_extensions.Annotated[ - typing.Optional[typing.List[PersonPageResponseEntityOwnedChannelsItem]], - FieldMetadata(alias="ownedChannels"), - pydantic.Field( - alias="ownedChannels", - description="Up to 10 channels this entity owns, most videos first. Null when it owns none.", - ), - ] = None - """ - Up to 10 channels this entity owns, most videos first. Null when it owns none. - """ - - owned_products: typing_extensions.Annotated[ - typing.Optional[typing.List[PersonPageResponseEntityOwnedProductsItem]], - FieldMetadata(alias="ownedProducts"), - pydantic.Field( - alias="ownedProducts", - description="Up to 10 products this entity owns, most mentions first. Null when it owns none.", - ), - ] = None - """ - Up to 10 products this entity owns, most mentions first. Null when it owns none. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_entity_owned_channels_item.py b/src/arcmira/types/person_page_response_entity_owned_channels_item.py deleted file mode 100644 index 13a5b49..0000000 --- a/src/arcmira/types/person_page_response_entity_owned_channels_item.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class PersonPageResponseEntityOwnedChannelsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Entity name. - """ - - display_name: str = pydantic.Field() - """ - Label that tells a homonym apart, e.g. "AdQuick (channel)". Equals name when no label is needed. - """ - - type: str = pydantic.Field() - """ - Entity type: channel or product. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the entity page on arcmira.com. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Entity image URL. Null until resolved."), - ] = None - """ - Entity image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - video_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="videoCount"), - pydantic.Field(alias="videoCount", description="Channels only: media rows published by the channel."), - ] = None - """ - Channels only: media rows published by the channel. - """ - - mention_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="mentionCount"), - pydantic.Field(alias="mentionCount", description="Products only: appearance rows of the product."), - ] = None - """ - Products only: appearance rows of the product. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_entity_owned_products_item.py b/src/arcmira/types/person_page_response_entity_owned_products_item.py deleted file mode 100644 index d4b5698..0000000 --- a/src/arcmira/types/person_page_response_entity_owned_products_item.py +++ /dev/null @@ -1,82 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class PersonPageResponseEntityOwnedProductsItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Entity name. - """ - - display_name: str = pydantic.Field() - """ - Label that tells a homonym apart, e.g. "AdQuick (channel)". Equals name when no label is needed. - """ - - type: str = pydantic.Field() - """ - Entity type: channel or product. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the entity page on arcmira.com. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Entity image URL. Null until resolved."), - ] = None - """ - Entity image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - video_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="videoCount"), - pydantic.Field(alias="videoCount", description="Channels only: media rows published by the channel."), - ] = None - """ - Channels only: media rows published by the channel. - """ - - mention_count: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="mentionCount"), - pydantic.Field(alias="mentionCount", description="Products only: appearance rows of the product."), - ] = None - """ - Products only: appearance rows of the product. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_mentions_by_month_item.py b/src/arcmira/types/person_page_response_mentions_by_month_item.py deleted file mode 100644 index 9f01e6a..0000000 --- a/src/arcmira/types/person_page_response_mentions_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class PersonPageResponseMentionsByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_mentions_item.py b/src/arcmira/types/person_page_response_mentions_item.py deleted file mode 100644 index 9bc6859..0000000 --- a/src/arcmira/types/person_page_response_mentions_item.py +++ /dev/null @@ -1,134 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_mentions_item_platform import PersonPageResponseMentionsItemPlatform -from .person_page_response_mentions_item_sentiment import PersonPageResponseMentionsItemSentiment -from .person_page_response_mentions_item_type import PersonPageResponseMentionsItemType -from .published_excerpt import PublishedExcerpt - - -class PersonPageResponseMentionsItem(UniversalBaseModel): - id: str = pydantic.Field() - """ - Raw appearance row id (the first row for the video), as a string. - """ - - date: str = pydantic.Field() - """ - Publish date as locale display text, or Unknown. - """ - - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Publish timestamp. Null when unknown."), - ] = None - """ - Publish timestamp. Null when unknown. - """ - - title: typing.Optional[str] = pydantic.Field(default=None) - """ - Video title. - """ - - channel: str = pydantic.Field() - """ - Source channel name, or Unknown Channel. - """ - - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id of the source channel. Null when unknown."), - ] = None - """ - YouTube channel id of the source channel. Null when unknown. - """ - - channel_handle: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelHandle"), - pydantic.Field(alias="channelHandle", description="YouTube handle of the source channel. Null when unknown."), - ] = None - """ - YouTube handle of the source channel. Null when unknown. - """ - - platform: PersonPageResponseMentionsItemPlatform = pydantic.Field() - """ - Always youtube. - """ - - thumbnail: str = pydantic.Field() - """ - Video thumbnail URL. - """ - - thumbnail_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="thumbnailUrl"), - pydantic.Field(alias="thumbnailUrl", description="Video thumbnail URL. Same value as thumbnail."), - ] - """ - Video thumbnail URL. Same value as thumbnail. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - duration: typing.Optional[float] = pydantic.Field(default=None) - """ - Video length in seconds. Null when unknown. - """ - - type: PersonPageResponseMentionsItemType = pydantic.Field() - """ - host or guest on an appearance row (host when the person hosts the channel); mention on a mention row. - """ - - context: str = pydantic.Field() - """ - Description of the moment, or "No description available". - """ - - sentiment: PersonPageResponseMentionsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - timestamp: str = pydantic.Field() - """ - Earliest start timestamp as MM:SS text, or "Full Episode" when none. - """ - - raw_timestamp: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="rawTimestamp"), - pydantic.Field(alias="rawTimestamp", description="Earliest start timestamp as stored. Null when none."), - ] = None - """ - Earliest start timestamp as stored. Null when none. - """ - - excerpt: typing.Optional[PublishedExcerpt] = None - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_mentions_item_platform.py b/src/arcmira/types/person_page_response_mentions_item_platform.py deleted file mode 100644 index 2c9bfef..0000000 --- a/src/arcmira/types/person_page_response_mentions_item_platform.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseMentionsItemPlatform = typing.Union[typing.Literal["youtube"], typing.Any] diff --git a/src/arcmira/types/person_page_response_mentions_item_sentiment.py b/src/arcmira/types/person_page_response_mentions_item_sentiment.py deleted file mode 100644 index 5debc83..0000000 --- a/src/arcmira/types/person_page_response_mentions_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseMentionsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/person_page_response_mentions_item_type.py b/src/arcmira/types/person_page_response_mentions_item_type.py deleted file mode 100644 index 53b5cfe..0000000 --- a/src/arcmira/types/person_page_response_mentions_item_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseMentionsItemType = typing.Union[typing.Literal["host", "guest", "mention"], typing.Any] diff --git a/src/arcmira/types/person_page_response_people_item.py b/src/arcmira/types/person_page_response_people_item.py deleted file mode 100644 index 1405ea6..0000000 --- a/src/arcmira/types/person_page_response_people_item.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_people_item_role import PersonPageResponsePeopleItemRole -from .person_page_response_people_item_sentiment import PersonPageResponsePeopleItemSentiment - - -class PersonPageResponsePeopleItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: PersonPageResponsePeopleItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - role: PersonPageResponsePeopleItemRole = pydantic.Field() - """ - Always Connection. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_people_item_role.py b/src/arcmira/types/person_page_response_people_item_role.py deleted file mode 100644 index 5556b0e..0000000 --- a/src/arcmira/types/person_page_response_people_item_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponsePeopleItemRole = typing.Union[typing.Literal["Connection"], typing.Any] diff --git a/src/arcmira/types/person_page_response_people_item_sentiment.py b/src/arcmira/types/person_page_response_people_item_sentiment.py deleted file mode 100644 index 1d155a2..0000000 --- a/src/arcmira/types/person_page_response_people_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponsePeopleItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/person_page_response_products_item.py b/src/arcmira/types/person_page_response_products_item.py deleted file mode 100644 index 6360415..0000000 --- a/src/arcmira/types/person_page_response_products_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_products_item_sentiment import PersonPageResponseProductsItemSentiment - - -class PersonPageResponseProductsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: PersonPageResponseProductsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_products_item_sentiment.py b/src/arcmira/types/person_page_response_products_item_sentiment.py deleted file mode 100644 index 5c23163..0000000 --- a/src/arcmira/types/person_page_response_products_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseProductsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/person_page_response_role_edge.py b/src/arcmira/types/person_page_response_role_edge.py deleted file mode 100644 index 6a7c2d5..0000000 --- a/src/arcmira/types/person_page_response_role_edge.py +++ /dev/null @@ -1,71 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .person_page_response_role_edge_label import PersonPageResponseRoleEdgeLabel -from .person_page_response_role_edge_role import PersonPageResponseRoleEdgeRole - - -class PersonPageResponseRoleEdge(UniversalBaseModel): - """ - A verified CEO role for the person. Null when none is verified. - """ - - role: PersonPageResponseRoleEdgeRole = pydantic.Field() - """ - Always ceo. - """ - - label: PersonPageResponseRoleEdgeLabel = pydantic.Field() - """ - Display label for the role line. - """ - - name: str = pydantic.Field() - """ - Name of the organization the person leads. - """ - - href: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative link to the organization page. Null when none. - """ - - receipt: typing.Optional[str] = pydantic.Field(default=None) - """ - Display line naming the evidence for the role. Null when there is none to show. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Organization logo URL. Null until resolved."), - ] = None - """ - Organization logo URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", - description="When the image pipeline last checked the organization. Null until checked.", - ), - ] = None - """ - When the image pipeline last checked the organization. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_role_edge_label.py b/src/arcmira/types/person_page_response_role_edge_label.py deleted file mode 100644 index 3074b22..0000000 --- a/src/arcmira/types/person_page_response_role_edge_label.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseRoleEdgeLabel = typing.Union[typing.Literal["CEO of", "CO-CEO of"], typing.Any] diff --git a/src/arcmira/types/person_page_response_role_edge_role.py b/src/arcmira/types/person_page_response_role_edge_role.py deleted file mode 100644 index 87290b8..0000000 --- a/src/arcmira/types/person_page_response_role_edge_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseRoleEdgeRole = typing.Union[typing.Literal["ceo"], typing.Any] diff --git a/src/arcmira/types/person_page_response_stats.py b/src/arcmira/types/person_page_response_stats.py deleted file mode 100644 index e595a0f..0000000 --- a/src/arcmira/types/person_page_response_stats.py +++ /dev/null @@ -1,91 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class PersonPageResponseStats(UniversalBaseModel): - """ - Headline numbers for the person. - """ - - velocity: int = pydantic.Field() - """ - Appearances in the last 90 days. - """ - - sentiment: float = pydantic.Field() - """ - Reserved. Always 0. - """ - - reach: str = pydantic.Field() - """ - Total views of the person's appearances as display text, e.g. 1.2M. - """ - - reach_raw: typing_extensions.Annotated[ - float, - FieldMetadata(alias="reachRaw"), - pydantic.Field(alias="reachRaw", description="Total views of the person's appearances."), - ] - """ - Total views of the person's appearances. - """ - - total: int = pydantic.Field() - """ - Total appearances. - """ - - total_mentions: typing_extensions.Annotated[ - int, - FieldMetadata(alias="totalMentions"), - pydantic.Field(alias="totalMentions", description="Total media that mention the person."), - ] - """ - Total media that mention the person. - """ - - total_mention_views: typing_extensions.Annotated[ - float, - FieldMetadata(alias="totalMentionViews"), - pydantic.Field(alias="totalMentionViews", description="Total views of the media that mention the person."), - ] - """ - Total views of the media that mention the person. - """ - - mention_reach: typing_extensions.Annotated[ - str, - FieldMetadata(alias="mentionReach"), - pydantic.Field(alias="mentionReach", description="totalMentionViews as display text."), - ] - """ - totalMentionViews as display text. - """ - - latest_media_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="latestMediaAt"), - pydantic.Field( - alias="latestMediaAt", - description="Newest publish date among the counted media. Null when none. The freshness gate does not withhold it.", - ), - ] = None - """ - Newest publish date among the counted media. Null when none. The freshness gate does not withhold it. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_topics_item.py b/src/arcmira/types/person_page_response_topics_item.py deleted file mode 100644 index a854d8e..0000000 --- a/src/arcmira/types/person_page_response_topics_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .person_page_response_topics_item_sentiment import PersonPageResponseTopicsItemSentiment - - -class PersonPageResponseTopicsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: PersonPageResponseTopicsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/person_page_response_topics_item_sentiment.py b/src/arcmira/types/person_page_response_topics_item_sentiment.py deleted file mode 100644 index a2e70c5..0000000 --- a/src/arcmira/types/person_page_response_topics_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PersonPageResponseTopicsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/product_page_response.py b/src/arcmira/types/product_page_response.py deleted file mode 100644 index 15b90a3..0000000 --- a/src/arcmira/types/product_page_response.py +++ /dev/null @@ -1,83 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_page_mention import EntityPageMention -from .exposure_meta import ExposureMeta -from .product_page_response_channels_item import ProductPageResponseChannelsItem -from .product_page_response_entity import ProductPageResponseEntity -from .product_page_response_mentions_by_month_item import ProductPageResponseMentionsByMonthItem -from .product_page_response_opportunities import ProductPageResponseOpportunities -from .product_page_response_organizations_item import ProductPageResponseOrganizationsItem -from .product_page_response_people_item import ProductPageResponsePeopleItem -from .product_page_response_stats import ProductPageResponseStats -from .product_page_response_topics_item import ProductPageResponseTopicsItem - - -class ProductPageResponse(UniversalBaseModel): - entity: ProductPageResponseEntity = pydantic.Field() - """ - The product and who owns it. - """ - - stats: ProductPageResponseStats = pydantic.Field() - """ - Headline numbers and section totals for the product. - """ - - opportunities: ProductPageResponseOpportunities = pydantic.Field() - """ - Reserved for an advertiser view. Both counts are disabled and always 0; 0 means not computed, not a real count. - """ - - mentions_by_month: typing_extensions.Annotated[ - typing.List[ProductPageResponseMentionsByMonthItem], - FieldMetadata(alias="mentionsByMonth"), - pydantic.Field( - alias="mentionsByMonth", - description="Media mentioning the product per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Media mentioning the product per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - topics: typing.List[ProductPageResponseTopicsItem] = pydantic.Field() - """ - Up to 10 co-occurring topics, highest count first. - """ - - people: typing.List[ProductPageResponsePeopleItem] = pydantic.Field() - """ - Up to 10 co-occurring people, highest count first. - """ - - organizations: typing.List[ProductPageResponseOrganizationsItem] = pydantic.Field() - """ - Up to 10 co-occurring organizations, highest count first. - """ - - channels: typing.List[ProductPageResponseChannelsItem] = pydantic.Field() - """ - Up to 6 channels that mention the product, highest count first. - """ - - mentions: typing.List[EntityPageMention] = pydantic.Field() - """ - Newest media that mention the entity, one row per video, cut to the plan's media rows. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_channels_item.py b/src/arcmira/types/product_page_response_channels_item.py deleted file mode 100644 index 6d98db9..0000000 --- a/src/arcmira/types/product_page_response_channels_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .product_page_response_channels_item_sentiment import ProductPageResponseChannelsItemSentiment - - -class ProductPageResponseChannelsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Channel name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media on this channel that mention the entity. Null when the plan hides counts. - """ - - sentiment: ProductPageResponseChannelsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_channels_item_sentiment.py b/src/arcmira/types/product_page_response_channels_item_sentiment.py deleted file mode 100644 index 3793826..0000000 --- a/src/arcmira/types/product_page_response_channels_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductPageResponseChannelsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/product_page_response_entity.py b/src/arcmira/types/product_page_response_entity.py deleted file mode 100644 index e7a23cd..0000000 --- a/src/arcmira/types/product_page_response_entity.py +++ /dev/null @@ -1,92 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .product_page_response_entity_owner import ProductPageResponseEntityOwner -from .product_page_response_entity_parent_org import ProductPageResponseEntityParentOrg -from .product_page_response_entity_type import ProductPageResponseEntityType - - -class ProductPageResponseEntity(UniversalBaseModel): - """ - The product and who owns it. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Product name. - """ - - type: ProductPageResponseEntityType = pydantic.Field() - """ - Always product. - """ - - category: str = pydantic.Field() - """ - The stored platform, or Software when none is stored. - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until resolved."), - ] = None - """ - Logo URL. Null until resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - is_priority: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPriority"), - pydantic.Field(alias="isPriority", description="True when the entity is flagged priority."), - ] - """ - True when the entity is flagged priority. - """ - - owner: typing.Optional[ProductPageResponseEntityOwner] = pydantic.Field(default=None) - """ - The organization or person that owns this entity. Null when no owner is recorded. - """ - - parent_org: typing_extensions.Annotated[ - typing.Optional[ProductPageResponseEntityParentOrg], - FieldMetadata(alias="parentOrg"), - pydantic.Field( - alias="parentOrg", - description="Legacy: the organization recorded as owning the product in entity relations. Null when none. Prefer owner.", - ), - ] = None - """ - Legacy: the organization recorded as owning the product in entity relations. Null when none. Prefer owner. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_entity_owner.py b/src/arcmira/types/product_page_response_entity_owner.py deleted file mode 100644 index 0ec8172..0000000 --- a/src/arcmira/types/product_page_response_entity_owner.py +++ /dev/null @@ -1,41 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ProductPageResponseEntityOwner(UniversalBaseModel): - """ - The organization or person that owns this entity. Null when no owner is recorded. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id of the owner. - """ - - name: str = pydantic.Field() - """ - Owner name. - """ - - type: str = pydantic.Field() - """ - Owner entity type: organization (legacy rows may read company or brand) or person. - """ - - route: str = pydantic.Field() - """ - Site-relative route of the owner page on arcmira.com. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_entity_parent_org.py b/src/arcmira/types/product_page_response_entity_parent_org.py deleted file mode 100644 index 6bb6769..0000000 --- a/src/arcmira/types/product_page_response_entity_parent_org.py +++ /dev/null @@ -1,31 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class ProductPageResponseEntityParentOrg(UniversalBaseModel): - """ - Legacy: the organization recorded as owning the product in entity relations. Null when none. Prefer owner. - """ - - name: str = pydantic.Field() - """ - Parent organization name. - """ - - slug: str = pydantic.Field() - """ - The name lowercased with spaces as hyphens. Not guaranteed to match the organization page slug. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_entity_type.py b/src/arcmira/types/product_page_response_entity_type.py deleted file mode 100644 index 2363f37..0000000 --- a/src/arcmira/types/product_page_response_entity_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductPageResponseEntityType = typing.Union[typing.Literal["product"], typing.Any] diff --git a/src/arcmira/types/product_page_response_mentions_by_month_item.py b/src/arcmira/types/product_page_response_mentions_by_month_item.py deleted file mode 100644 index b37e072..0000000 --- a/src/arcmira/types/product_page_response_mentions_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ProductPageResponseMentionsByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_opportunities.py b/src/arcmira/types/product_page_response_opportunities.py deleted file mode 100644 index a2b524a..0000000 --- a/src/arcmira/types/product_page_response_opportunities.py +++ /dev/null @@ -1,37 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ProductPageResponseOpportunities(UniversalBaseModel): - """ - Reserved for an advertiser view. Both counts are disabled and always 0; 0 means not computed, not a real count. - """ - - evangelists: float = pydantic.Field() - """ - Not computed. Always 0. - """ - - ad_inventory: typing_extensions.Annotated[ - float, - FieldMetadata(alias="adInventory"), - pydantic.Field(alias="adInventory", description="Not computed. Always 0."), - ] - """ - Not computed. Always 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_organizations_item.py b/src/arcmira/types/product_page_response_organizations_item.py deleted file mode 100644 index b6fdbba..0000000 --- a/src/arcmira/types/product_page_response_organizations_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .product_page_response_organizations_item_sentiment import ProductPageResponseOrganizationsItemSentiment - - -class ProductPageResponseOrganizationsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ProductPageResponseOrganizationsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_organizations_item_sentiment.py b/src/arcmira/types/product_page_response_organizations_item_sentiment.py deleted file mode 100644 index ae8686a..0000000 --- a/src/arcmira/types/product_page_response_organizations_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductPageResponseOrganizationsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/product_page_response_people_item.py b/src/arcmira/types/product_page_response_people_item.py deleted file mode 100644 index 882d3bf..0000000 --- a/src/arcmira/types/product_page_response_people_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .product_page_response_people_item_sentiment import ProductPageResponsePeopleItemSentiment - - -class ProductPageResponsePeopleItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ProductPageResponsePeopleItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_people_item_sentiment.py b/src/arcmira/types/product_page_response_people_item_sentiment.py deleted file mode 100644 index 3d84542..0000000 --- a/src/arcmira/types/product_page_response_people_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductPageResponsePeopleItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/product_page_response_stats.py b/src/arcmira/types/product_page_response_stats.py deleted file mode 100644 index c357e41..0000000 --- a/src/arcmira/types/product_page_response_stats.py +++ /dev/null @@ -1,84 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class ProductPageResponseStats(UniversalBaseModel): - """ - Headline numbers and section totals for the product. - """ - - velocity: typing.Optional[int] = pydantic.Field(default=None) - """ - Mentions in the last 90 days. Null when the plan hides it. - """ - - sentiment: float = pydantic.Field() - """ - Reserved. Always 0. - """ - - reach: str = pydantic.Field() - """ - Total views as display text, e.g. 1.2M. - """ - - reach_raw: typing_extensions.Annotated[ - float, - FieldMetadata(alias="reachRaw"), - pydantic.Field(alias="reachRaw", description="Total views of the media that mention the product."), - ] - """ - Total views of the media that mention the product. - """ - - total: int = pydantic.Field() - """ - Total media that mention the product. - """ - - latest_media_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="latestMediaAt"), - pydantic.Field( - alias="latestMediaAt", - description="Newest publish date among the counted media. Null when none. The freshness gate does not withhold it.", - ), - ] = None - """ - Newest publish date among the counted media. Null when none. The freshness gate does not withhold it. - """ - - people: int = pydantic.Field() - """ - Total co-occurring people. - """ - - topics: int = pydantic.Field() - """ - Total co-occurring topics. - """ - - organizations: int = pydantic.Field() - """ - Total co-occurring organizations. - """ - - channels: int = pydantic.Field() - """ - Total channels that mention the product. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_topics_item.py b/src/arcmira/types/product_page_response_topics_item.py deleted file mode 100644 index abea1b8..0000000 --- a/src/arcmira/types/product_page_response_topics_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .product_page_response_topics_item_sentiment import ProductPageResponseTopicsItemSentiment - - -class ProductPageResponseTopicsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: ProductPageResponseTopicsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/product_page_response_topics_item_sentiment.py b/src/arcmira/types/product_page_response_topics_item_sentiment.py deleted file mode 100644 index 7cdbeff..0000000 --- a/src/arcmira/types/product_page_response_topics_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ProductPageResponseTopicsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/channel_page_response_stats.py b/src/arcmira/types/publication_window.py similarity index 50% rename from src/arcmira/types/channel_page_response_stats.py rename to src/arcmira/types/publication_window.py index aa36ae9..430c247 100644 --- a/src/arcmira/types/channel_page_response_stats.py +++ b/src/arcmira/types/publication_window.py @@ -6,29 +6,19 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class ChannelPageResponseStats(UniversalBaseModel): +class PublicationWindow(UniversalBaseModel): """ - Header vitals. Open on every plan. + The publication window the answer covers, [after, before) in UTC, normalized from after and before. """ - velocity: int = pydantic.Field() + after: typing.Optional[str] = pydantic.Field(default=None) """ - Videos published in the last 90 days. + Inclusive start as an ISO instant. Null when the window has no start. """ - sentiment: float = pydantic.Field() + before: typing.Optional[str] = pydantic.Field(default=None) """ - Reserved. Always 0.5. - """ - - reach: str = pydantic.Field() - """ - Average views per video as display text, e.g. 48.2K. - """ - - total: int = pydantic.Field() - """ - Indexed videos of the channel. + Exclusive end as an ISO instant. Null when the window has no end. Earlier than the before you sent when your plan's freshness gate cut the window. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/published_excerpt.py b/src/arcmira/types/published_excerpt.py deleted file mode 100644 index 0b41ded..0000000 --- a/src/arcmira/types/published_excerpt.py +++ /dev/null @@ -1,106 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .published_excerpt_public_source_class import PublishedExcerptPublicSourceClass - - -class PublishedExcerpt(UniversalBaseModel): - """ - A speakerless excerpt naming the entity in this media, when one is active. Mention rows only. - """ - - id: str = pydantic.Field() - """ - Published excerpt id. - """ - - exact_text: typing_extensions.Annotated[ - str, - FieldMetadata(alias="exactText"), - pydantic.Field(alias="exactText", description="The transcript span that names the entity."), - ] - """ - The transcript span that names the entity. - """ - - context_before: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="contextBefore"), - pydantic.Field(alias="contextBefore", description="Transcript text immediately before the span."), - ] = None - """ - Transcript text immediately before the span. - """ - - context_after: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="contextAfter"), - pydantic.Field(alias="contextAfter", description="Transcript text immediately after the span."), - ] = None - """ - Transcript text immediately after the span. - """ - - term: str = pydantic.Field() - """ - The surface form that matched in the transcript, which may be an approved alias. - """ - - term_char_start: typing_extensions.Annotated[ - int, - FieldMetadata(alias="termCharStart"), - pydantic.Field(alias="termCharStart", description="Character offset where term starts."), - ] - """ - Character offset where term starts. - """ - - term_char_end: typing_extensions.Annotated[ - int, - FieldMetadata(alias="termCharEnd"), - pydantic.Field(alias="termCharEnd", description="Character offset where term ends."), - ] - """ - Character offset where term ends. - """ - - start_seconds: typing_extensions.Annotated[ - float, - FieldMetadata(alias="startSeconds"), - pydantic.Field(alias="startSeconds", description="Span start in the video, in seconds."), - ] - """ - Span start in the video, in seconds. - """ - - end_seconds: typing_extensions.Annotated[ - typing.Optional[float], - FieldMetadata(alias="endSeconds"), - pydantic.Field(alias="endSeconds", description="Span end in the video, in seconds."), - ] = None - """ - Span end in the video, in seconds. - """ - - public_source_class: typing_extensions.Annotated[ - PublishedExcerptPublicSourceClass, - FieldMetadata(alias="publicSourceClass"), - pydantic.Field(alias="publicSourceClass", description="Transcript source class of the excerpt."), - ] - """ - Transcript source class of the excerpt. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/published_excerpt_public_source_class.py b/src/arcmira/types/published_excerpt_public_source_class.py deleted file mode 100644 index 8f44f67..0000000 --- a/src/arcmira/types/published_excerpt_public_source_class.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -PublishedExcerptPublicSourceClass = typing.Union[ - typing.Literal["arcmira_premium_excerpt", "creator_captions", "third_party_quick"], typing.Any -] diff --git a/src/arcmira/types/recommendation.py b/src/arcmira/types/recommendation.py index e89ba69..4f86de9 100644 --- a/src/arcmira/types/recommendation.py +++ b/src/arcmira/types/recommendation.py @@ -3,8 +3,11 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata from .entity_ref import EntityRef +from .recommendation_class import RecommendationClass from .recommendation_media import RecommendationMedia @@ -14,36 +17,28 @@ class Recommendation(UniversalBaseModel): Public recommendation id in the form "com_{n}". """ - recommendation_id: int = pydantic.Field() + class_: typing_extensions.Annotated[ + RecommendationClass, + FieldMetadata(alias="class"), + pydantic.Field( + alias="class", + description="Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).", + ), + ] """ - Raw integer id of the recommendation row. Same number as in the "com_{n}" public id. - """ - - mention_class: str = pydantic.Field() - """ - Commercial mention classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention). + Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). """ entity: EntityRef media: RecommendationMedia - start_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds. - """ - - end_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds. - """ - start_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - Start position in the video in integer SECONDS, parsed from start_timestamp. Prefer this over the deprecated string field. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + Start position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ end_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - End position in the video in integer SECONDS, parsed from end_timestamp. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + End position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ verbatim_quote: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/arcmira/types/recommendation_class.py b/src/arcmira/types/recommendation_class.py new file mode 100644 index 0000000..f00968a --- /dev/null +++ b/src/arcmira/types/recommendation_class.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RecommendationClass = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/types/recommendation_enrichment_item.py b/src/arcmira/types/recommendation_enrichment_item.py index 1226a26..c30260c 100644 --- a/src/arcmira/types/recommendation_enrichment_item.py +++ b/src/arcmira/types/recommendation_enrichment_item.py @@ -3,7 +3,10 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .recommendation_enrichment_item_class import RecommendationEnrichmentItemClass class RecommendationEnrichmentItem(UniversalBaseModel): @@ -12,9 +15,16 @@ class RecommendationEnrichmentItem(UniversalBaseModel): Public recommendation id in the form "com_{n}". """ - mention_class: str = pydantic.Field() + class_: typing_extensions.Annotated[ + RecommendationEnrichmentItemClass, + FieldMetadata(alias="class"), + pydantic.Field( + alias="class", + description="Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).", + ), + ] """ - Commercial mention classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention). + Commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). """ verbatim_quote: typing.Optional[str] = pydantic.Field(default=None) @@ -37,24 +47,14 @@ class RecommendationEnrichmentItem(UniversalBaseModel): Classifier confidence between 0 and 1. """ - start_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use start_seconds. - """ - - end_timestamp: str = pydantic.Field() - """ - DEPRECATED: prefer the numeric sibling field. This "MM:SS" (or "HH:MM:SS") string remains until a dated, changelog-announced removal (see /requests#versioning-and-deprecation). Use end_seconds. - """ - start_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - Start position in the video in integer SECONDS, parsed from start_timestamp. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + Start position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ end_seconds: typing.Optional[int] = pydantic.Field(default=None) """ - End position in the video in integer SECONDS, parsed from end_timestamp. 0 means "full episode / no specific moment" (the string sentinel "00:00"). Null when the string timestamp is null or unparseable. + End position in the video in integer seconds. 0 means "full episode / no specific moment". Null when the analyzer could not place it in time. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/recommendation_enrichment_item_class.py b/src/arcmira/types/recommendation_enrichment_item_class.py new file mode 100644 index 0000000..2ed7f92 --- /dev/null +++ b/src/arcmira/types/recommendation_enrichment_item_class.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RecommendationEnrichmentItemClass = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/types/recommendation_list_response.py b/src/arcmira/types/recommendation_list_response.py index 4d68a67..c5b72c3 100644 --- a/src/arcmira/types/recommendation_list_response.py +++ b/src/arcmira/types/recommendation_list_response.py @@ -4,12 +4,17 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .entity import Entity +from .publication_window import PublicationWindow from .recommendation import Recommendation -from .recommendation_list_response_entity import RecommendationListResponseEntity class RecommendationListResponse(UniversalBaseModel): - data: typing.List[Recommendation] + recommendations: typing.List[Recommendation] = pydantic.Field() + """ + Newest first. + """ + has_more: bool = pydantic.Field() """ True when more rows exist past this page. @@ -20,10 +25,8 @@ class RecommendationListResponse(UniversalBaseModel): Opaque cursor for the next page. Null on the last page. """ - entity: RecommendationListResponseEntity = pydantic.Field() - """ - The resolved entity the recommendations belong to. - """ + entity: Entity + window: PublicationWindow if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 diff --git a/src/arcmira/types/recommendation_list_response_entity.py b/src/arcmira/types/recommendation_list_response_entity.py deleted file mode 100644 index 7c8eeb3..0000000 --- a/src/arcmira/types/recommendation_list_response_entity.py +++ /dev/null @@ -1,111 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class RecommendationListResponseEntity(UniversalBaseModel): - """ - The resolved entity the recommendations belong to. - """ - - id: str = pydantic.Field() - """ - Public entity id in the form "ent_{n}". Always the canonical entity id. - """ - - numeric_id: int = pydantic.Field() - """ - Raw integer database id of the canonical entity. Prefer the public "ent_{n}" id in requests. - """ - - canonical_id: str = pydantic.Field() - """ - Public id of the canonical entity. Identical to id. - """ - - name: str = pydantic.Field() - """ - Canonical entity name. - """ - - type: str = pydantic.Field() - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - platform: typing.Optional[str] = pydantic.Field(default=None) - """ - Source platform for channel entities, e.g. "youtube". Null unless the entity is platform-bound. - """ - - url: typing.Optional[str] = pydantic.Field(default=None) - """ - Canonical external URL for the entity. Null when none is known. - """ - - image_url: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity image URL. Null until an image has been resolved. - """ - - image_checked_at: typing.Optional[str] = pydantic.Field(default=None) - """ - Timestamp of the last image resolution attempt. Null until the image pipeline has visited this entity. - """ - - appearance_count: typing.Optional[int] = pydantic.Field(default=None) - """ - Number of indexed appearance/mention rows for this entity. 0 when never counted. - """ - - owner_entity_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Public id ("ent_{n}") of the owning entity, e.g. the organization behind a product. Null unless an ownership link exists. - """ - - is_canonical: bool = pydantic.Field() - """ - True when the id you supplied is the canonical entity. False when your id was merged into this canonical record. - """ - - merged_from_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Public id you supplied when it differs from the canonical entity, i.e. your id was merged into this record. Null unless a merge redirect happened. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - URL slug, the site's canonical id for every type but channel. Null when never slugged. - """ - - route: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative route of this entity's page on arcmira.com, e.g. "/org/ramp", "/person/jane-doe", "/yt/@TBPNLive". Null for a type the site has no page for. - """ - - page: typing.Optional[str] = pydantic.Field(default=None) - """ - The entity's page on arcmira.com, absolute. Link the name to it when you write the entity into an answer. Null for a type the site has no page for. - """ - - appearances_page: typing.Optional[str] = pydantic.Field(default=None) - """ - For a person, the list of their appearances on arcmira.com (their page opens on it). Null for every other type. - """ - - mentions_page: typing.Optional[str] = pydantic.Field(default=None) - """ - For a person, the list of mentions of them on arcmira.com, page/mentions. Null for every other type: their page is the mentions list already. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/search_resolve_response.py b/src/arcmira/types/search_resolve_response.py deleted file mode 100644 index 1166cbe..0000000 --- a/src/arcmira/types/search_resolve_response.py +++ /dev/null @@ -1,46 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .search_resolve_response_entity import SearchResolveResponseEntity - - -class SearchResolveResponse(UniversalBaseModel): - found: bool = pydantic.Field() - """ - True when the query resolved to exactly one entity. - """ - - entity: typing.Optional[SearchResolveResponseEntity] = pydantic.Field(default=None) - """ - The resolved entity. Absent when found is false. - """ - - route: typing.Optional[str] = pydantic.Field(default=None) - """ - Site-relative route for the entity on arcmira.com. Absent when found is false. - """ - - from_cache: typing_extensions.Annotated[ - typing.Optional[bool], - FieldMetadata(alias="fromCache"), - pydantic.Field( - alias="fromCache", description="True when the result was served from the 5-minute resolver cache." - ), - ] = None - """ - True when the result was served from the 5-minute resolver cache. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/search_resolve_response_entity.py b/src/arcmira/types/search_resolve_response_entity.py deleted file mode 100644 index 353d043..0000000 --- a/src/arcmira/types/search_resolve_response_entity.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class SearchResolveResponseEntity(UniversalBaseModel): - """ - The resolved entity. Absent when found is false. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. Use "ent_{id}" with the /v1/entities endpoints. - """ - - name: str = pydantic.Field() - """ - Canonical entity name. - """ - - type: str = pydantic.Field() - """ - Entity type. Values: person (an individual), organization (a company or institution; legacy rows may read company or brand), product (a product or service), topic (a subject or theme), channel (a media source such as a YouTube channel), unknown (type was never classified). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_page_response_recommendations_summary.py b/src/arcmira/types/slack_integration_list_response.py similarity index 54% rename from src/arcmira/types/channel_page_response_recommendations_summary.py rename to src/arcmira/types/slack_integration_list_response.py index a1d5be7..6196a56 100644 --- a/src/arcmira/types/channel_page_response_recommendations_summary.py +++ b/src/arcmira/types/slack_integration_list_response.py @@ -4,21 +4,13 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .slack_integration_list_response_integrations_item import SlackIntegrationListResponseIntegrationsItem -class ChannelPageResponseRecommendationsSummary(UniversalBaseModel): +class SlackIntegrationListResponse(UniversalBaseModel): + integrations: typing.List[SlackIntegrationListResponseIntegrationsItem] = pydantic.Field() """ - Sponsor teaser for the channel. - """ - - sponsor_count: int = pydantic.Field() - """ - Recurring sponsors of the channel. 0 when none. - """ - - top_sponsors: typing.Optional[typing.List[str]] = pydantic.Field(default=None) - """ - Names of the top three sponsors. Present only on a Pro+ plan when the channel has sponsors. + Active Slack workspaces connected to the account, ordered by workspace name. Empty when none is connected: connect one in the dashboard first. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/slack_integration_list_response_integrations_item.py b/src/arcmira/types/slack_integration_list_response_integrations_item.py new file mode 100644 index 0000000..5a903ea --- /dev/null +++ b/src/arcmira/types/slack_integration_list_response_integrations_item.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .slack_integration_list_response_integrations_item_channels_item import ( + SlackIntegrationListResponseIntegrationsItemChannelsItem, +) + + +class SlackIntegrationListResponseIntegrationsItem(UniversalBaseModel): + id: str = pydantic.Field() + """ + Slack integration id. Pass it as slack_integration_id when creating or updating a monitor with notify_slack: true. + """ + + team_name: str = pydantic.Field() + """ + The Slack workspace name. + """ + + default_channel_id: typing.Optional[str] = pydantic.Field(default=None) + """ + The channel the workspace install chose. A monitor with notify_slack and no slack_channel_id delivers here. Null when the install chose none; then pass slack_channel_id. + """ + + channels: typing.List[SlackIntegrationListResponseIntegrationsItemChannelsItem] = pydantic.Field() + """ + The channels Arcmira has stored for this workspace: today the install channel only. Arcmira does not list the workspace live. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/feedback_correction_result_recommendation_media_source_channel.py b/src/arcmira/types/slack_integration_list_response_integrations_item_channels_item.py similarity index 69% rename from src/arcmira/types/feedback_correction_result_recommendation_media_source_channel.py rename to src/arcmira/types/slack_integration_list_response_integrations_item_channels_item.py index f50316e..616d62f 100644 --- a/src/arcmira/types/feedback_correction_result_recommendation_media_source_channel.py +++ b/src/arcmira/types/slack_integration_list_response_integrations_item_channels_item.py @@ -6,19 +6,15 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class FeedbackCorrectionResultRecommendationMediaSourceChannel(UniversalBaseModel): - """ - The channel entity that published the video. Null when the video has not been linked to a channel entity. - """ - +class SlackIntegrationListResponseIntegrationsItemChannelsItem(UniversalBaseModel): id: str = pydantic.Field() """ - Public entity id ("ent_{n}") of the source channel. + Slack channel id. """ name: typing.Optional[str] = pydantic.Field(default=None) """ - Source channel name. + Channel name as stored at install time, without the #. Null when not stored. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/speaker_identification_submitted_response.py b/src/arcmira/types/speaker_identification_submitted_response.py deleted file mode 100644 index d66ef85..0000000 --- a/src/arcmira/types/speaker_identification_submitted_response.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .speaker_identification_submitted_response_identification import ( - SpeakerIdentificationSubmittedResponseIdentification, -) - - -class SpeakerIdentificationSubmittedResponse(UniversalBaseModel): - identification: SpeakerIdentificationSubmittedResponseIdentification = pydantic.Field() - """ - The stored pending identification. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/speaker_identification_submitted_response_identification.py b/src/arcmira/types/speaker_identification_submitted_response_identification.py deleted file mode 100644 index 33f68e1..0000000 --- a/src/arcmira/types/speaker_identification_submitted_response_identification.py +++ /dev/null @@ -1,64 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .speaker_identification_submitted_response_identification_entity import ( - SpeakerIdentificationSubmittedResponseIdentificationEntity, -) -from .speaker_identification_submitted_response_identification_status import ( - SpeakerIdentificationSubmittedResponseIdentificationStatus, -) - - -class SpeakerIdentificationSubmittedResponseIdentification(UniversalBaseModel): - """ - The stored pending identification. - """ - - id: int = pydantic.Field() - """ - Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/speakers/{id}. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - speaker_id: typing_extensions.Annotated[ - int, - FieldMetadata(alias="speakerId"), - pydantic.Field( - alias="speakerId", description="The speaker id you sent, from the transcript read's speakers[]." - ), - ] - """ - The speaker id you sent, from the transcript read's speakers[]. - """ - - entity: SpeakerIdentificationSubmittedResponseIdentificationEntity = pydantic.Field() - """ - The person the speaker now points at: the entity you named, an existing person matching the name, or a newly created one. - """ - - status: SpeakerIdentificationSubmittedResponseIdentificationStatus = pydantic.Field() - """ - Review status. Always pending on submit. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/speaker_identification_submitted_response_identification_entity.py b/src/arcmira/types/speaker_identification_submitted_response_identification_entity.py deleted file mode 100644 index 72f80be..0000000 --- a/src/arcmira/types/speaker_identification_submitted_response_identification_entity.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class SpeakerIdentificationSubmittedResponseIdentificationEntity(UniversalBaseModel): - """ - The person the speaker now points at: the entity you named, an existing person matching the name, or a newly created one. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id of the person. - """ - - name: str = pydantic.Field() - """ - Person name. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - Person slug. Null when never slugged. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/speaker_identification_submitted_response_identification_status.py b/src/arcmira/types/speaker_identification_submitted_response_identification_status.py deleted file mode 100644 index 5d884e3..0000000 --- a/src/arcmira/types/speaker_identification_submitted_response_identification_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -SpeakerIdentificationSubmittedResponseIdentificationStatus = typing.Union[ - typing.Literal["pending", "approved", "rejected", "withdrawn", "reverted"], typing.Any -] diff --git a/src/arcmira/types/team_member.py b/src/arcmira/types/team_member.py deleted file mode 100644 index 63ce503..0000000 --- a/src/arcmira/types/team_member.py +++ /dev/null @@ -1,49 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .team_member_role import TeamMemberRole -from .team_member_seat_type import TeamMemberSeatType - - -class TeamMember(UniversalBaseModel): - user_id: str = pydantic.Field() - """ - Id of the member's user account. - """ - - name: str = pydantic.Field() - """ - Member display name. - """ - - email: str = pydantic.Field() - """ - Member email address. - """ - - role: TeamMemberRole = pydantic.Field() - """ - Team role. Values: owner, admin, member, unpaid_admin. Unpaid admins manage the team without a paid seat and have no product access. - """ - - seat_type: TeamMemberSeatType = pydantic.Field() - """ - Seat type. Values: standard (5,000 rows/month included), premium (25,000 rows/month included), free (the Unpaid Admin seat: no product access). - """ - - joined_at: typing.Optional[str] = pydantic.Field(default=None) - """ - When the member joined the team. Null when unknown. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/team_member_role.py b/src/arcmira/types/team_member_role.py deleted file mode 100644 index 84119c8..0000000 --- a/src/arcmira/types/team_member_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TeamMemberRole = typing.Union[typing.Literal["owner", "admin", "member", "unpaid_admin"], typing.Any] diff --git a/src/arcmira/types/team_member_seat_type.py b/src/arcmira/types/team_member_seat_type.py deleted file mode 100644 index fb6790e..0000000 --- a/src/arcmira/types/team_member_seat_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TeamMemberSeatType = typing.Union[typing.Literal["standard", "premium", "free"], typing.Any] diff --git a/src/arcmira/types/team_member_spend.py b/src/arcmira/types/team_member_spend.py deleted file mode 100644 index 898678f..0000000 --- a/src/arcmira/types/team_member_spend.py +++ /dev/null @@ -1,59 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .team_member_spend_role import TeamMemberSpendRole -from .team_member_spend_seat_type import TeamMemberSpendSeatType - - -class TeamMemberSpend(UniversalBaseModel): - user_id: str = pydantic.Field() - """ - Id of the member's user account. - """ - - name: str = pydantic.Field() - """ - Member display name. - """ - - email: str = pydantic.Field() - """ - Member email address. - """ - - role: TeamMemberSpendRole = pydantic.Field() - """ - Team role. Values: owner, admin, member, unpaid_admin. Unpaid admins manage the team without a paid seat and have no product access. - """ - - seat_type: TeamMemberSpendSeatType = pydantic.Field() - """ - Seat type. Values: standard (5,000 rows/month included), premium (25,000 rows/month included), free (the Unpaid Admin seat: no product access). - """ - - rows_used: int = pydantic.Field() - """ - Rows this member consumed in the current period. - """ - - on_demand_spend_cents: int = pydantic.Field() - """ - Account-wide on-demand overage spend for this member in the current period, in US cents. - """ - - on_demand_enabled: bool = pydantic.Field() - """ - The member account's on-demand preference. Effective admission also depends on the team setting and remaining spend limit. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/team_member_spend_role.py b/src/arcmira/types/team_member_spend_role.py deleted file mode 100644 index 34a4de7..0000000 --- a/src/arcmira/types/team_member_spend_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TeamMemberSpendRole = typing.Union[typing.Literal["owner", "admin", "member", "unpaid_admin"], typing.Any] diff --git a/src/arcmira/types/team_member_spend_seat_type.py b/src/arcmira/types/team_member_spend_seat_type.py deleted file mode 100644 index f49d644..0000000 --- a/src/arcmira/types/team_member_spend_seat_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TeamMemberSpendSeatType = typing.Union[typing.Literal["standard", "premium", "free"], typing.Any] diff --git a/src/arcmira/types/team_members_response.py b/src/arcmira/types/team_members_response.py deleted file mode 100644 index 943c0fa..0000000 --- a/src/arcmira/types/team_members_response.py +++ /dev/null @@ -1,26 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .team_member import TeamMember -from .team_members_response_team import TeamMembersResponseTeam - - -class TeamMembersResponse(UniversalBaseModel): - data: typing.List[TeamMember] = pydantic.Field() - """ - Active members, earliest join first. Removed members are excluded. - """ - - team: TeamMembersResponseTeam - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/team_usage_event.py b/src/arcmira/types/team_usage_event.py deleted file mode 100644 index b9a6830..0000000 --- a/src/arcmira/types/team_usage_event.py +++ /dev/null @@ -1,67 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class TeamUsageEvent(UniversalBaseModel): - id: int = pydantic.Field() - """ - Usage event id. - """ - - user_id: typing.Optional[str] = pydantic.Field(default=None) - """ - Id of the member who generated the event. - """ - - action: str = pydantic.Field() - """ - Usage action, e.g. entity_view, search, export. - """ - - entity_type: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity type the event touched, when applicable. - """ - - entity_name: typing.Optional[str] = pydantic.Field(default=None) - """ - Entity name the event touched, when applicable. - """ - - total_rows: int = pydantic.Field() - """ - Total rows returned by the request. - """ - - premium_rows: int = pydantic.Field() - """ - Rows charged against the member's allowance (total minus free rows). - """ - - request_path: typing.Optional[str] = pydantic.Field(default=None) - """ - API path that generated the event. - """ - - is_overage: bool = pydantic.Field() - """ - True when this event billed on-demand rows past the included allowance. - """ - - created_at: typing.Optional[str] = pydantic.Field(default=None) - """ - When the event was recorded. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/team_usage_events_response.py b/src/arcmira/types/team_usage_events_response.py deleted file mode 100644 index 06f760a..0000000 --- a/src/arcmira/types/team_usage_events_response.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .team_usage_event import TeamUsageEvent - - -class TeamUsageEventsResponse(UniversalBaseModel): - data: typing.List[TeamUsageEvent] = pydantic.Field() - """ - Usage events across all team members, newest first. - """ - - has_more: bool = pydantic.Field() - """ - True when another page exists. - """ - - next_cursor: typing.Optional[str] = pydantic.Field(default=None) - """ - Opaque cursor for the next page. Null on the last page. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response.py b/src/arcmira/types/topic_page_response.py deleted file mode 100644 index ae1caed..0000000 --- a/src/arcmira/types/topic_page_response.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_page_mention import EntityPageMention -from .exposure_meta import ExposureMeta -from .topic_page_response_channels_item import TopicPageResponseChannelsItem -from .topic_page_response_companies_item import TopicPageResponseCompaniesItem -from .topic_page_response_entity import TopicPageResponseEntity -from .topic_page_response_mentions_by_month_item import TopicPageResponseMentionsByMonthItem -from .topic_page_response_products_item import TopicPageResponseProductsItem -from .topic_page_response_related_topics_item import TopicPageResponseRelatedTopicsItem -from .topic_page_response_stats import TopicPageResponseStats -from .topic_page_response_voices_item import TopicPageResponseVoicesItem - - -class TopicPageResponse(UniversalBaseModel): - entity: TopicPageResponseEntity = pydantic.Field() - """ - The topic. - """ - - stats: TopicPageResponseStats = pydantic.Field() - """ - Headline numbers and section totals for the topic. - """ - - mentions_by_month: typing_extensions.Annotated[ - typing.List[TopicPageResponseMentionsByMonthItem], - FieldMetadata(alias="mentionsByMonth"), - pydantic.Field( - alias="mentionsByMonth", - description="Media mentioning the topic per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart.", - ), - ] - """ - Media mentioning the topic per month. Twelve months ending this month, oldest first. Empty when the plan withholds the chart. - """ - - related_topics: typing_extensions.Annotated[ - typing.List[TopicPageResponseRelatedTopicsItem], - FieldMetadata(alias="relatedTopics"), - pydantic.Field(alias="relatedTopics", description="Co-occurring topics, highest count first."), - ] - """ - Co-occurring topics, highest count first. - """ - - voices: typing.List[TopicPageResponseVoicesItem] = pydantic.Field() - """ - People who appeared in media with the topic, highest count first. - """ - - companies: typing.List[TopicPageResponseCompaniesItem] = pydantic.Field() - """ - Co-occurring organizations, highest count first. - """ - - products: typing.List[TopicPageResponseProductsItem] = pydantic.Field() - """ - Co-occurring products, highest count first. - """ - - channels: typing.List[TopicPageResponseChannelsItem] = pydantic.Field() - """ - Channels that mention the topic, highest count first. - """ - - mentions: typing.List[EntityPageMention] = pydantic.Field() - """ - Newest media that mention the entity, one row per video, cut to the plan's media rows. - """ - - meta: typing_extensions.Annotated[ExposureMeta, FieldMetadata(alias="_meta"), pydantic.Field(alias="_meta")] - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_channels_item.py b/src/arcmira/types/topic_page_response_channels_item.py deleted file mode 100644 index 662a7af..0000000 --- a/src/arcmira/types/topic_page_response_channels_item.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .topic_page_response_channels_item_sentiment import TopicPageResponseChannelsItemSentiment - - -class TopicPageResponseChannelsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Channel name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media on this channel that mention the entity. Null when the plan hides counts. - """ - - sentiment: TopicPageResponseChannelsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - slug: typing.Optional[str] = pydantic.Field(default=None) - """ - The channel's page key on arcmira.com: its @handle when known, else its UC id. Null when neither is known. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_channels_item_sentiment.py b/src/arcmira/types/topic_page_response_channels_item_sentiment.py deleted file mode 100644 index 16d6408..0000000 --- a/src/arcmira/types/topic_page_response_channels_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseChannelsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/topic_page_response_companies_item.py b/src/arcmira/types/topic_page_response_companies_item.py deleted file mode 100644 index 077accf..0000000 --- a/src/arcmira/types/topic_page_response_companies_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .topic_page_response_companies_item_sentiment import TopicPageResponseCompaniesItemSentiment - - -class TopicPageResponseCompaniesItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: TopicPageResponseCompaniesItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_companies_item_sentiment.py b/src/arcmira/types/topic_page_response_companies_item_sentiment.py deleted file mode 100644 index 6000440..0000000 --- a/src/arcmira/types/topic_page_response_companies_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseCompaniesItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/topic_page_response_entity.py b/src/arcmira/types/topic_page_response_entity.py deleted file mode 100644 index da5713c..0000000 --- a/src/arcmira/types/topic_page_response_entity.py +++ /dev/null @@ -1,68 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .topic_page_response_entity_type import TopicPageResponseEntityType - - -class TopicPageResponseEntity(UniversalBaseModel): - """ - The topic. - """ - - id: int = pydantic.Field() - """ - Raw integer entity id. - """ - - name: str = pydantic.Field() - """ - Topic name. - """ - - type: TopicPageResponseEntityType = pydantic.Field() - """ - Always topic. - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Topic image URL. Null until resolved."), - ] = None - """ - Topic image URL. Null until resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - is_priority: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPriority"), - pydantic.Field(alias="isPriority", description="True when the entity is flagged priority."), - ] - """ - True when the entity is flagged priority. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_entity_type.py b/src/arcmira/types/topic_page_response_entity_type.py deleted file mode 100644 index 074816d..0000000 --- a/src/arcmira/types/topic_page_response_entity_type.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseEntityType = typing.Union[typing.Literal["topic"], typing.Any] diff --git a/src/arcmira/types/topic_page_response_mentions_by_month_item.py b/src/arcmira/types/topic_page_response_mentions_by_month_item.py deleted file mode 100644 index 5ab7d05..0000000 --- a/src/arcmira/types/topic_page_response_mentions_by_month_item.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class TopicPageResponseMentionsByMonthItem(UniversalBaseModel): - month: str = pydantic.Field() - """ - Three-letter month name, e.g. Jan. - """ - - year_month: typing_extensions.Annotated[ - str, FieldMetadata(alias="yearMonth"), pydantic.Field(alias="yearMonth", description="The month as YYYY-MM.") - ] - """ - The month as YYYY-MM. - """ - - count: int = pydantic.Field() - """ - Media that month. Months inside the withheld window read 0. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_products_item.py b/src/arcmira/types/topic_page_response_products_item.py deleted file mode 100644 index 555c396..0000000 --- a/src/arcmira/types/topic_page_response_products_item.py +++ /dev/null @@ -1,55 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .topic_page_response_products_item_sentiment import TopicPageResponseProductsItemSentiment - - -class TopicPageResponseProductsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Entity name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: TopicPageResponseProductsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - logo_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoUrl"), - pydantic.Field(alias="logoUrl", description="Logo URL. Null until an image has been resolved."), - ] = None - """ - Logo URL. Null until an image has been resolved. - """ - - logo_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="logoCheckedAt"), - pydantic.Field( - alias="logoCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_products_item_sentiment.py b/src/arcmira/types/topic_page_response_products_item_sentiment.py deleted file mode 100644 index 80ebc7e..0000000 --- a/src/arcmira/types/topic_page_response_products_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseProductsItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/topic_page_response_related_topics_item.py b/src/arcmira/types/topic_page_response_related_topics_item.py deleted file mode 100644 index ce6e123..0000000 --- a/src/arcmira/types/topic_page_response_related_topics_item.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .topic_page_response_related_topics_item_sentiment import TopicPageResponseRelatedTopicsItemSentiment - - -class TopicPageResponseRelatedTopicsItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Topic name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: TopicPageResponseRelatedTopicsItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_related_topics_item_sentiment.py b/src/arcmira/types/topic_page_response_related_topics_item_sentiment.py deleted file mode 100644 index 558b77e..0000000 --- a/src/arcmira/types/topic_page_response_related_topics_item_sentiment.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseRelatedTopicsItemSentiment = typing.Union[ - typing.Literal["positive", "neutral", "negative"], typing.Any -] diff --git a/src/arcmira/types/topic_page_response_stats.py b/src/arcmira/types/topic_page_response_stats.py deleted file mode 100644 index c960c72..0000000 --- a/src/arcmira/types/topic_page_response_stats.py +++ /dev/null @@ -1,94 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata - - -class TopicPageResponseStats(UniversalBaseModel): - """ - Headline numbers and section totals for the topic. - """ - - velocity: typing.Optional[int] = pydantic.Field(default=None) - """ - Mentions in the last 7 days. Null when the plan hides it. - """ - - velocity90d: typing.Optional[int] = pydantic.Field(default=None) - """ - Mentions in the last 90 days. Null when the plan hides it. - """ - - sentiment: float = pydantic.Field() - """ - Reserved. Always 0. - """ - - reach: str = pydantic.Field() - """ - Total views as display text, e.g. 1.2M. - """ - - reach_raw: typing_extensions.Annotated[ - float, - FieldMetadata(alias="reachRaw"), - pydantic.Field(alias="reachRaw", description="Total views of the media that mention the topic."), - ] - """ - Total views of the media that mention the topic. - """ - - total: int = pydantic.Field() - """ - Total media that mention the topic. - """ - - latest_media_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="latestMediaAt"), - pydantic.Field( - alias="latestMediaAt", - description="Newest publish date among the counted media. Null when none. The freshness gate does not withhold it.", - ), - ] = None - """ - Newest publish date among the counted media. Null when none. The freshness gate does not withhold it. - """ - - people: int = pydantic.Field() - """ - Total people who appeared in media with the topic. - """ - - organizations: int = pydantic.Field() - """ - Total co-occurring organizations. - """ - - products: int = pydantic.Field() - """ - Total co-occurring products. - """ - - topics: int = pydantic.Field() - """ - Total related topics. - """ - - channels: int = pydantic.Field() - """ - Total channels that mention the topic. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_voices_item.py b/src/arcmira/types/topic_page_response_voices_item.py deleted file mode 100644 index 338e610..0000000 --- a/src/arcmira/types/topic_page_response_voices_item.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .topic_page_response_voices_item_role import TopicPageResponseVoicesItemRole -from .topic_page_response_voices_item_sentiment import TopicPageResponseVoicesItemSentiment - - -class TopicPageResponseVoicesItem(UniversalBaseModel): - name: str = pydantic.Field() - """ - Person name. - """ - - count: typing.Optional[int] = pydantic.Field(default=None) - """ - Media shared with the entity. Null when the plan hides counts (_meta.showingFullData false). - """ - - sentiment: TopicPageResponseVoicesItemSentiment = pydantic.Field() - """ - Average sentiment label. Values: positive (average above 0.2), negative (below -0.2), neutral (otherwise, or no score). - """ - - image_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageUrl"), - pydantic.Field(alias="imageUrl", description="Person image URL. Null until an image has been resolved."), - ] = None - """ - Person image URL. Null until an image has been resolved. - """ - - image_checked_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="imageCheckedAt"), - pydantic.Field( - alias="imageCheckedAt", description="When the image pipeline last checked this entity. Null until checked." - ), - ] = None - """ - When the image pipeline last checked this entity. Null until checked. - """ - - role: TopicPageResponseVoicesItemRole = pydantic.Field() - """ - Always Commentator. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/topic_page_response_voices_item_role.py b/src/arcmira/types/topic_page_response_voices_item_role.py deleted file mode 100644 index 29d11e0..0000000 --- a/src/arcmira/types/topic_page_response_voices_item_role.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseVoicesItemRole = typing.Union[typing.Literal["Commentator"], typing.Any] diff --git a/src/arcmira/types/topic_page_response_voices_item_sentiment.py b/src/arcmira/types/topic_page_response_voices_item_sentiment.py deleted file mode 100644 index 060b2bd..0000000 --- a/src/arcmira/types/topic_page_response_voices_item_sentiment.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TopicPageResponseVoicesItemSentiment = typing.Union[typing.Literal["positive", "neutral", "negative"], typing.Any] diff --git a/src/arcmira/types/tracker.py b/src/arcmira/types/tracker.py index e8a3998..beda20f 100644 --- a/src/arcmira/types/tracker.py +++ b/src/arcmira/types/tracker.py @@ -3,9 +3,7 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class Tracker(UniversalBaseModel): @@ -14,99 +12,47 @@ class Tracker(UniversalBaseModel): Tracker id in the form "trk_{hex}". """ - entity_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="entityName"), - pydantic.Field(alias="entityName", description="The tracked entity name, as submitted."), - ] + entity_name: str = pydantic.Field() """ The tracked entity name, as submitted. """ - entity_type: typing_extensions.Annotated[ - str, - FieldMetadata(alias="entityType"), - pydantic.Field( - alias="entityType", - description="The tracked entity type. Values: person, organization, product, topic, channel.", - ), - ] + entity_type: str = pydantic.Field() """ The tracked entity type. Values: person, organization, product, topic, channel. """ - display_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="displayName"), - pydantic.Field( - alias="displayName", description="User-facing display name. Falls back to entityName when not customized." - ), - ] + display_name: str = pydantic.Field() """ - User-facing display name. Falls back to entityName when not customized. + User-facing display name. Falls back to entity_name when not customized. """ - notify_email: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="notifyEmail"), - pydantic.Field( - alias="notifyEmail", description="True when this tracker delivers by email (default true at creation)." - ), - ] + notify_email: bool = pydantic.Field() """ True when this tracker delivers by email (default true at creation). """ - notify_webhook: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="notifyWebhook"), - pydantic.Field( - alias="notifyWebhook", description="True when this tracker has a per-tracker webhook override enabled." - ), - ] + notify_webhook: bool = pydantic.Field() """ True when this tracker has a per-tracker webhook override enabled. """ - notify_slack: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="notifySlack"), - pydantic.Field( - alias="notifySlack", description="True when this tracker has a per-tracker Slack override enabled." - ), - ] + notify_slack: bool = pydantic.Field() """ True when this tracker has a per-tracker Slack override enabled. """ - webhook_url: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="webhookUrl"), - pydantic.Field( - alias="webhookUrl", - description="Per-tracker webhook destination override. Null when the tracker uses its monitor's delivery settings.", - ), - ] = None + webhook_url: typing.Optional[str] = pydantic.Field(default=None) """ - Per-tracker webhook destination override. Null when the tracker uses its monitor's delivery settings. + Per-tracker webhook destination override. Null when the tracker uses its monitor's delivery settings. Absent when the tracker is in a team monitor the caller does not own. """ - slack_channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="slackChannelId"), - pydantic.Field(alias="slackChannelId", description="Per-tracker Slack channel override. Null when not set."), - ] = None + slack_channel_id: typing.Optional[str] = pydantic.Field(default=None) """ Per-tracker Slack channel override. Null when not set. """ - slack_integration_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="slackIntegrationId"), - pydantic.Field( - alias="slackIntegrationId", description="Per-tracker Slack integration override. Null when not set." - ), - ] = None + slack_integration_id: typing.Optional[str] = pydantic.Field(default=None) """ Per-tracker Slack integration override. Null when not set. """ @@ -116,87 +62,47 @@ class Tracker(UniversalBaseModel): Optional matching filters as submitted. Null when none were set. """ - is_paused: typing_extensions.Annotated[ - bool, - FieldMetadata(alias="isPaused"), - pydantic.Field(alias="isPaused", description="True when the tracker is paused."), - ] + paused: bool = pydantic.Field() """ True when the tracker is paused. """ - paused_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="pausedAt"), - pydantic.Field(alias="pausedAt", description="When the tracker was paused. Null unless paused."), - ] = None + paused_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker was paused. Null unless paused. """ - last_notified_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="lastNotifiedAt"), - pydantic.Field( - alias="lastNotifiedAt", description="When the tracker last produced an alert. Null until the first alert." - ), - ] = None + last_notified_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker last produced an alert. Null until the first alert. """ - created_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="createdAt"), - pydantic.Field(alias="createdAt", description="When the tracker was created."), - ] + created_at: str = pydantic.Field() """ When the tracker was created. """ - updated_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="updatedAt"), - pydantic.Field(alias="updatedAt", description="When the tracker was last updated."), - ] = None + updated_at: typing.Optional[str] = pydantic.Field(default=None) """ When the tracker was last updated. """ - monitor_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="monitorId"), - pydantic.Field( - alias="monitorId", description="The monitor this tracker belongs to. Absent for standalone trackers." - ), - ] = None + monitor_id: typing.Optional[str] = pydantic.Field(default=None) """ The monitor this tracker belongs to. Absent for standalone trackers. """ - email_delivery_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="emailDeliveryCount"), - pydantic.Field(alias="emailDeliveryCount", description="Email deliveries in the current billing period."), - ] + email_delivery_count: int = pydantic.Field() """ Email deliveries in the current billing period. """ - webhook_delivery_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="webhookDeliveryCount"), - pydantic.Field(alias="webhookDeliveryCount", description="Webhook deliveries in the current billing period."), - ] + webhook_delivery_count: int = pydantic.Field() """ Webhook deliveries in the current billing period. """ - slack_delivery_count: typing_extensions.Annotated[ - int, - FieldMetadata(alias="slackDeliveryCount"), - pydantic.Field(alias="slackDeliveryCount", description="Slack deliveries in the current billing period."), - ] + slack_delivery_count: int = pydantic.Field() """ Slack deliveries in the current billing period. """ diff --git a/src/arcmira/types/transcript_edit_submitted_response.py b/src/arcmira/types/transcript_edit_submitted_response.py deleted file mode 100644 index 2f12011..0000000 --- a/src/arcmira/types/transcript_edit_submitted_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_edit_submitted_response_edit import TranscriptEditSubmittedResponseEdit - - -class TranscriptEditSubmittedResponse(UniversalBaseModel): - edit: TranscriptEditSubmittedResponseEdit = pydantic.Field() - """ - The stored pending edit. It replaces any pending edit you had on the same line. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_edit_submitted_response_edit.py b/src/arcmira/types/transcript_edit_submitted_response_edit.py deleted file mode 100644 index f9dd8ed..0000000 --- a/src/arcmira/types/transcript_edit_submitted_response_edit.py +++ /dev/null @@ -1,61 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .transcript_edit_submitted_response_edit_status import TranscriptEditSubmittedResponseEditStatus - - -class TranscriptEditSubmittedResponseEdit(UniversalBaseModel): - """ - The stored pending edit. It replaces any pending edit you had on the same line. - """ - - id: int = pydantic.Field() - """ - Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/edits/{id}. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - segment_index: typing_extensions.Annotated[ - int, - FieldMetadata(alias="segmentIndex"), - pydantic.Field(alias="segmentIndex", description="The line index the edit targets."), - ] - """ - The line index the edit targets. - """ - - corrected_text: typing_extensions.Annotated[ - str, - FieldMetadata(alias="correctedText"), - pydantic.Field(alias="correctedText", description="The corrected line text as stored, trimmed."), - ] - """ - The corrected line text as stored, trimmed. - """ - - status: TranscriptEditSubmittedResponseEditStatus = pydantic.Field() - """ - Review status. Always pending on submit. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_edit_submitted_response_edit_status.py b/src/arcmira/types/transcript_edit_submitted_response_edit_status.py deleted file mode 100644 index d24d83d..0000000 --- a/src/arcmira/types/transcript_edit_submitted_response_edit_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptEditSubmittedResponseEditStatus = typing.Union[ - typing.Literal["pending", "approved", "rejected", "withdrawn"], typing.Any -] diff --git a/src/arcmira/types/transcript_preparation_required.py b/src/arcmira/types/transcript_preparation_required.py deleted file mode 100644 index 5573a59..0000000 --- a/src/arcmira/types/transcript_preparation_required.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_preparation_required_action import TranscriptPreparationRequiredAction -from .transcript_preparation_required_last_attempt import TranscriptPreparationRequiredLastAttempt -from .transcript_preparation_required_quality import TranscriptPreparationRequiredQuality -from .transcript_preparation_required_quote import TranscriptPreparationRequiredQuote - - -class TranscriptPreparationRequired(UniversalBaseModel): - quality: TranscriptPreparationRequiredQuality - video_id: str - quote: typing.Optional[TranscriptPreparationRequiredQuote] = pydantic.Field(default=None) - """ - Null when the video has no known duration or is longer than 12 hours; the POST refuses it for the same reason. - """ - - action: TranscriptPreparationRequiredAction = pydantic.Field() - """ - The one request that prepares Premium from included credits and moves no money. - """ - - last_attempt: typing.Optional[TranscriptPreparationRequiredLastAttempt] = pydantic.Field(default=None) - """ - The most recent failed or refunded purchase of this video, when there is one. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_action.py b/src/arcmira/types/transcript_preparation_required_action.py deleted file mode 100644 index c382fff..0000000 --- a/src/arcmira/types/transcript_preparation_required_action.py +++ /dev/null @@ -1,27 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_preparation_required_action_body import TranscriptPreparationRequiredActionBody -from .transcript_preparation_required_action_method import TranscriptPreparationRequiredActionMethod - - -class TranscriptPreparationRequiredAction(UniversalBaseModel): - """ - The one request that prepares Premium from included credits and moves no money. - """ - - method: TranscriptPreparationRequiredActionMethod - url: str - body: TranscriptPreparationRequiredActionBody - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_action_body.py b/src/arcmira/types/transcript_preparation_required_action_body.py deleted file mode 100644 index 240d660..0000000 --- a/src/arcmira/types/transcript_preparation_required_action_body.py +++ /dev/null @@ -1,19 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class TranscriptPreparationRequiredActionBody(UniversalBaseModel): - video_id: str - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_action_method.py b/src/arcmira/types/transcript_preparation_required_action_method.py deleted file mode 100644 index a85aed9..0000000 --- a/src/arcmira/types/transcript_preparation_required_action_method.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptPreparationRequiredActionMethod = typing.Union[typing.Literal["POST"], typing.Any] diff --git a/src/arcmira/types/transcript_preparation_required_last_attempt.py b/src/arcmira/types/transcript_preparation_required_last_attempt.py deleted file mode 100644 index cbcf1b6..0000000 --- a/src/arcmira/types/transcript_preparation_required_last_attempt.py +++ /dev/null @@ -1,24 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class TranscriptPreparationRequiredLastAttempt(UniversalBaseModel): - """ - The most recent failed or refunded purchase of this video, when there is one. - """ - - status: str - error: str - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_quality.py b/src/arcmira/types/transcript_preparation_required_quality.py deleted file mode 100644 index 3365af0..0000000 --- a/src/arcmira/types/transcript_preparation_required_quality.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptPreparationRequiredQuality = typing.Union[typing.Literal["premium"], typing.Any] diff --git a/src/arcmira/types/transcript_preparation_required_quote.py b/src/arcmira/types/transcript_preparation_required_quote.py deleted file mode 100644 index c9f9651..0000000 --- a/src/arcmira/types/transcript_preparation_required_quote.py +++ /dev/null @@ -1,38 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_preparation_required_quote_charge import TranscriptPreparationRequiredQuoteCharge - - -class TranscriptPreparationRequiredQuote(UniversalBaseModel): - """ - Null when the video has no known duration or is longer than 12 hours; the POST refuses it for the same reason. - """ - - rows: int = pydantic.Field() - """ - Whole-video row-equivalent price. 0 when a prior unlock makes it free. - """ - - charge: TranscriptPreparationRequiredQuoteCharge - eligible: bool = pydantic.Field() - """ - False when the account cannot buy right now; the POST then answers with the reason. - """ - - max_on_demand_cents: int = pydantic.Field() - """ - On-demand money in whole cents the purchase would need. 0 means included credits cover it. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_quote_charge.py b/src/arcmira/types/transcript_preparation_required_quote_charge.py deleted file mode 100644 index 9fa132e..0000000 --- a/src/arcmira/types/transcript_preparation_required_quote_charge.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .transcript_preparation_required_quote_charge_from import TranscriptPreparationRequiredQuoteChargeFrom -from .transcript_preparation_required_quote_charge_unit import TranscriptPreparationRequiredQuoteChargeUnit - - -class TranscriptPreparationRequiredQuoteCharge(UniversalBaseModel): - unit: TranscriptPreparationRequiredQuoteChargeUnit - amount: float = pydantic.Field() - """ - Credits the purchase would charge. - """ - - from_: typing_extensions.Annotated[ - TranscriptPreparationRequiredQuoteChargeFrom, - FieldMetadata(alias="from"), - pydantic.Field(alias="from", description="Where those credits would come from at the current balance."), - ] - """ - Where those credits would come from at the current balance. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_quote_charge_unit.py b/src/arcmira/types/transcript_preparation_required_quote_charge_unit.py deleted file mode 100644 index 150d249..0000000 --- a/src/arcmira/types/transcript_preparation_required_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptPreparationRequiredQuoteChargeUnit = typing.Union[typing.Literal["credits"], typing.Any] diff --git a/src/arcmira/types/transcript_request_submit_response.py b/src/arcmira/types/transcript_request_submit_response.py deleted file mode 100644 index 16932a7..0000000 --- a/src/arcmira/types/transcript_request_submit_response.py +++ /dev/null @@ -1,24 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_job import TranscriptJob - - -class TranscriptRequestSubmitResponse(UniversalBaseModel): - job: TranscriptJob - existing: bool = pydantic.Field() - """ - True when a request for this video already existed (in flight or ready) and was returned instead of creating a new one. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_response_access.py b/src/arcmira/types/transcript_response_access.py index 6379722..53249c5 100644 --- a/src/arcmira/types/transcript_response_access.py +++ b/src/arcmira/types/transcript_response_access.py @@ -5,6 +5,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .error_resource import ErrorResource +from .transcript_response_access_details import TranscriptResponseAccessDetails from .transcript_response_access_gate import TranscriptResponseAccessGate from .transcript_response_access_reason import TranscriptResponseAccessReason from .transcript_response_access_type import TranscriptResponseAccessType @@ -67,6 +68,11 @@ class TranscriptResponseAccess(UniversalBaseModel): On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. """ + details: typing.Optional[TranscriptResponseAccessDetails] = pydantic.Field(default=None) + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + doc_url: str request_id: str diff --git a/src/arcmira/types/transcript_response_access_details.py b/src/arcmira/types/transcript_response_access_details.py new file mode 100644 index 0000000..024f1c4 --- /dev/null +++ b/src/arcmira/types/transcript_response_access_details.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .transcript_response_access_details_quote import TranscriptResponseAccessDetailsQuote + + +class TranscriptResponseAccessDetails(UniversalBaseModel): + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + + quote: typing.Optional[TranscriptResponseAccessDetailsQuote] = pydantic.Field(default=None) + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + existing_request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. + """ + + existing_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_response_access_details_quote.py b/src/arcmira/types/transcript_response_access_details_quote.py new file mode 100644 index 0000000..39b9da5 --- /dev/null +++ b/src/arcmira/types/transcript_response_access_details_quote.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .transcript_quote import TranscriptQuote +from .transcript_response_access_details_quote_charge import TranscriptResponseAccessDetailsQuoteCharge + + +class TranscriptResponseAccessDetailsQuote(TranscriptQuote): + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + charge: typing.Optional[TranscriptResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) + """ + The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_response_access_details_quote_charge.py b/src/arcmira/types/transcript_response_access_details_quote_charge.py new file mode 100644 index 0000000..bb01d49 --- /dev/null +++ b/src/arcmira/types/transcript_response_access_details_quote_charge.py @@ -0,0 +1,36 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .transcript_response_access_details_quote_charge_from import TranscriptResponseAccessDetailsQuoteChargeFrom +from .transcript_response_access_details_quote_charge_unit import TranscriptResponseAccessDetailsQuoteChargeUnit + + +class TranscriptResponseAccessDetailsQuoteCharge(UniversalBaseModel): + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + unit: TranscriptResponseAccessDetailsQuoteChargeUnit + amount: float + from_: typing_extensions.Annotated[ + TranscriptResponseAccessDetailsQuoteChargeFrom, + FieldMetadata(alias="from"), + pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), + ] + """ + Where the charge would come from at the current balance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_preparation_required_quote_charge_from.py b/src/arcmira/types/transcript_response_access_details_quote_charge_from.py similarity index 70% rename from src/arcmira/types/transcript_preparation_required_quote_charge_from.py rename to src/arcmira/types/transcript_response_access_details_quote_charge_from.py index 11b348d..0575305 100644 --- a/src/arcmira/types/transcript_preparation_required_quote_charge_from.py +++ b/src/arcmira/types/transcript_response_access_details_quote_charge_from.py @@ -2,6 +2,6 @@ import typing -TranscriptPreparationRequiredQuoteChargeFrom = typing.Union[ +TranscriptResponseAccessDetailsQuoteChargeFrom = typing.Union[ typing.Literal["included", "on_demand", "mixed"], typing.Any ] diff --git a/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py b/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py new file mode 100644 index 0000000..b328221 --- /dev/null +++ b/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/transcript_response_speakers_item.py b/src/arcmira/types/transcript_response_speakers_item.py index 32115c0..d2d03f2 100644 --- a/src/arcmira/types/transcript_response_speakers_item.py +++ b/src/arcmira/types/transcript_response_speakers_item.py @@ -17,9 +17,9 @@ class TranscriptResponseSpeakersItem(UniversalBaseModel): The identified person, or Speaker 1, Speaker 2 and so on for a voice nobody has identified yet. """ - entity_id: typing.Optional[int] = pydantic.Field(default=None) + entity_id: typing.Optional[str] = pydantic.Field(default=None) """ - Raw entity id of the identified person. Null when the speaker is unidentified. + Public entity id ("ent_{n}") of the identified person. Null when the speaker is unidentified. """ confidence: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/arcmira/types/transcript_result.py b/src/arcmira/types/transcript_result.py index 62b56bd..8dedaee 100644 --- a/src/arcmira/types/transcript_result.py +++ b/src/arcmira/types/transcript_result.py @@ -10,10 +10,6 @@ from .caption_track import CaptionTrack from .transcript_job import TranscriptJob from .transcript_pending_quality import TranscriptPendingQuality -from .transcript_preparation_required_action import TranscriptPreparationRequiredAction -from .transcript_preparation_required_last_attempt import TranscriptPreparationRequiredLastAttempt -from .transcript_preparation_required_quality import TranscriptPreparationRequiredQuality -from .transcript_preparation_required_quote import TranscriptPreparationRequiredQuote from .transcript_response_access import TranscriptResponseAccess from .transcript_response_lines_item import TranscriptResponseLinesItem from .transcript_response_paragraphs_item import TranscriptResponseParagraphsItem @@ -52,24 +48,6 @@ class Config: extra = pydantic.Extra.allow -class TranscriptResult_PreparationRequired(UniversalBaseModel): - state: typing.Literal["preparation_required"] = "preparation_required" - quality: TranscriptPreparationRequiredQuality - video_id: str - quote: typing.Optional[TranscriptPreparationRequiredQuote] = None - action: TranscriptPreparationRequiredAction - last_attempt: typing.Optional[TranscriptPreparationRequiredLastAttempt] = None - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow - - class TranscriptResult_Pending(UniversalBaseModel): state: typing.Literal["pending"] = "pending" quality: TranscriptPendingQuality @@ -87,6 +65,5 @@ class Config: TranscriptResult = typing_extensions.Annotated[ - typing.Union[TranscriptResult_Ready, TranscriptResult_PreparationRequired, TranscriptResult_Pending], - pydantic.Field(discriminator="state"), + typing.Union[TranscriptResult_Ready, TranscriptResult_Pending], pydantic.Field(discriminator="state") ] diff --git a/src/arcmira/types/transcript_search_chunk.py b/src/arcmira/types/transcript_search_chunk.py index 3e63158..ea8e1b0 100644 --- a/src/arcmira/types/transcript_search_chunk.py +++ b/src/arcmira/types/transcript_search_chunk.py @@ -3,9 +3,7 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata from .named_entity_ref import NamedEntityRef @@ -15,50 +13,27 @@ class TranscriptSearchChunk(UniversalBaseModel): Search index chunk id. Opaque. """ - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] + video_id: str = pydantic.Field() """ YouTube video id (11 characters). """ - channel_id: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelId"), - pydantic.Field(alias="channelId", description="YouTube channel id of the source channel."), - ] = None + channel_id: typing.Optional[str] = pydantic.Field(default=None) """ YouTube channel id of the source channel. """ - channel_name: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelName"), - pydantic.Field(alias="channelName", description="Source channel name."), - ] = None + channel_name: typing.Optional[str] = pydantic.Field(default=None) """ Source channel name. """ - channel_page: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="channelPage"), - pydantic.Field( - alias="channelPage", - description="The channel's page on arcmira.com, absolute. Null when no channel is known.", - ), - ] = None + channel_page: typing.Optional[str] = pydantic.Field(default=None) """ The channel's page on arcmira.com, absolute. Null when no channel is known. """ - video_title: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="videoTitle"), - pydantic.Field(alias="videoTitle", description="Video title."), - ] = None + video_title: typing.Optional[str] = pydantic.Field(default=None) """ Video title. """ @@ -73,20 +48,12 @@ class TranscriptSearchChunk(UniversalBaseModel): Transcript source class: arcmira_premium, creator_captions, or third_party_quick. """ - source_label: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="sourceLabel"), - pydantic.Field(alias="sourceLabel", description="Human label for source."), - ] = None + source_label: typing.Optional[str] = pydantic.Field(default=None) """ Human label for source. """ - published_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="publishedAt"), - pydantic.Field(alias="publishedAt", description="Video publish timestamp. Cite it as the date of the quote."), - ] = None + published_at: typing.Optional[str] = pydantic.Field(default=None) """ Video publish timestamp. Cite it as the date of the quote. """ @@ -96,43 +63,17 @@ class TranscriptSearchChunk(UniversalBaseModel): The spoken slice. Search results include text on every plan within the permitted publication-date window. """ - text_withheld: typing_extensions.Annotated[ - typing.Optional[bool], - FieldMetadata(alias="textWithheld"), - pydantic.Field( - alias="textWithheld", - description="Legacy field, no longer set. Since 2026-09-10, transcript search includes spoken text on every plan and limits results by publication date.", - ), - ] = None - """ - Legacy field, no longer set. Since 2026-09-10, transcript search includes spoken text on every plan and limits results by publication date. - """ - - start_seconds: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="startSeconds"), - pydantic.Field(alias="startSeconds", description="Offset of the slice in the video, in seconds."), - ] = None + start_seconds: typing.Optional[int] = pydantic.Field(default=None) """ Offset of the slice in the video, in seconds. """ - watch_url: typing_extensions.Annotated[ - str, - FieldMetadata(alias="watchUrl"), - pydantic.Field( - alias="watchUrl", description="Site-relative watch URL with the timestamp, e.g. /watch?v=...&t=4787." - ), - ] + watch_url: str = pydantic.Field() """ Site-relative watch URL with the timestamp, e.g. /watch?v=...&t=4787. """ - cite_line: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="citeLine"), - pydantic.Field(alias="citeLine", description="A ready citation line: title, clock, channel, date."), - ] = None + cite_line: typing.Optional[str] = pydantic.Field(default=None) """ A ready citation line: title, clock, channel, date. """ diff --git a/src/arcmira/types/transcript_search_response.py b/src/arcmira/types/transcript_search_response.py index 4aaacf2..25fa694 100644 --- a/src/arcmira/types/transcript_search_response.py +++ b/src/arcmira/types/transcript_search_response.py @@ -3,13 +3,13 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata +from .publication_window import PublicationWindow from .transcript_search_chunk import TranscriptSearchChunk from .transcript_search_response_access import TranscriptSearchResponseAccess from .transcript_search_response_filters import TranscriptSearchResponseFilters from .transcript_search_response_search_index import TranscriptSearchResponseSearchIndex +from .transcript_search_response_unlock import TranscriptSearchResponseUnlock class TranscriptSearchResponse(UniversalBaseModel): @@ -18,21 +18,18 @@ class TranscriptSearchResponse(UniversalBaseModel): The q parameter echoed back. """ - requested_k: typing_extensions.Annotated[ - int, FieldMetadata(alias="requestedK"), pydantic.Field(alias="requestedK", description="The limit applied.") - ] + limit: int = pydantic.Field() """ The limit applied. """ - returned_n: typing_extensions.Annotated[ - int, FieldMetadata(alias="returnedN"), pydantic.Field(alias="returnedN", description="Chunks returned.") - ] + returned: int = pydantic.Field() """ Chunks returned. """ filters: TranscriptSearchResponseFilters + window: PublicationWindow chunks: typing.List[TranscriptSearchChunk] = pydantic.Field() """ Ranked slices. Empty means no hit in the shows we index; say so, never search the open web. @@ -43,18 +40,14 @@ class TranscriptSearchResponse(UniversalBaseModel): True when some retrieval batches failed and these chunks are what survived. """ - failed_batches: typing_extensions.Annotated[ - typing.Optional[int], - FieldMetadata(alias="failedBatches"), - pydantic.Field(alias="failedBatches", description="How many batches failed when partial is true."), - ] = None + failed_batches: typing.Optional[int] = pydantic.Field(default=None) """ How many batches failed when partial is true. """ as_of: typing.Optional[str] = pydantic.Field(default=None) """ - Newest publishedAt among the chunks. Null when there are none. + Newest published_at among the chunks. Null when there are none. """ search_index: TranscriptSearchResponseSearchIndex = pydantic.Field() @@ -67,6 +60,11 @@ class TranscriptSearchResponse(UniversalBaseModel): The gate that reduced this response. Present only when something was withheld; carries the same code, gate, and unlock an outright refusal would. """ + unlock: typing.Optional[TranscriptSearchResponseUnlock] = pydantic.Field(default=None) + """ + Present when your plan's freshness gate cut the window and nothing older matched; note says so. + """ + note: str = pydantic.Field() """ One steering sentence for the agent reading this. diff --git a/src/arcmira/types/transcript_search_response_access.py b/src/arcmira/types/transcript_search_response_access.py index c6dd086..6bc0301 100644 --- a/src/arcmira/types/transcript_search_response_access.py +++ b/src/arcmira/types/transcript_search_response_access.py @@ -5,6 +5,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .error_resource import ErrorResource +from .transcript_search_response_access_details import TranscriptSearchResponseAccessDetails from .transcript_search_response_access_gate import TranscriptSearchResponseAccessGate from .transcript_search_response_access_reason import TranscriptSearchResponseAccessReason from .transcript_search_response_access_type import TranscriptSearchResponseAccessType @@ -67,6 +68,11 @@ class TranscriptSearchResponseAccess(UniversalBaseModel): On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. """ + details: typing.Optional[TranscriptSearchResponseAccessDetails] = pydantic.Field(default=None) + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + doc_url: str request_id: str diff --git a/src/arcmira/types/transcript_search_response_access_details.py b/src/arcmira/types/transcript_search_response_access_details.py new file mode 100644 index 0000000..5eb8f1e --- /dev/null +++ b/src/arcmira/types/transcript_search_response_access_details.py @@ -0,0 +1,37 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .transcript_search_response_access_details_quote import TranscriptSearchResponseAccessDetailsQuote + + +class TranscriptSearchResponseAccessDetails(UniversalBaseModel): + """ + Machine data the refusal carries for you to act on. Present only on the codes that name a field here. + """ + + quote: typing.Optional[TranscriptSearchResponseAccessDetailsQuote] = pydantic.Field(default=None) + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + existing_request_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. + """ + + existing_id: typing.Optional[str] = pydantic.Field(default=None) + """ + On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_search_response_access_details_quote.py b/src/arcmira/types/transcript_search_response_access_details_quote.py new file mode 100644 index 0000000..75ba75c --- /dev/null +++ b/src/arcmira/types/transcript_search_response_access_details_quote.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2 +from .transcript_quote import TranscriptQuote +from .transcript_search_response_access_details_quote_charge import TranscriptSearchResponseAccessDetailsQuoteCharge + + +class TranscriptSearchResponseAccessDetailsQuote(TranscriptQuote): + """ + The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + """ + + charge: typing.Optional[TranscriptSearchResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) + """ + The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge.py new file mode 100644 index 0000000..8762982 --- /dev/null +++ b/src/arcmira/types/transcript_search_response_access_details_quote_charge.py @@ -0,0 +1,40 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +import typing_extensions +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from ..core.serialization import FieldMetadata +from .transcript_search_response_access_details_quote_charge_from import ( + TranscriptSearchResponseAccessDetailsQuoteChargeFrom, +) +from .transcript_search_response_access_details_quote_charge_unit import ( + TranscriptSearchResponseAccessDetailsQuoteChargeUnit, +) + + +class TranscriptSearchResponseAccessDetailsQuoteCharge(UniversalBaseModel): + """ + What the purchase would charge at the current balance. Absent when no current price could be read. + """ + + unit: TranscriptSearchResponseAccessDetailsQuoteChargeUnit + amount: float + from_: typing_extensions.Annotated[ + TranscriptSearchResponseAccessDetailsQuoteChargeFrom, + FieldMetadata(alias="from"), + pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), + ] + """ + Where the charge would come from at the current balance. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py new file mode 100644 index 0000000..807ec89 --- /dev/null +++ b/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptSearchResponseAccessDetailsQuoteChargeFrom = typing.Union[ + typing.Literal["included", "on_demand", "mixed"], typing.Any +] diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py new file mode 100644 index 0000000..a8e8a31 --- /dev/null +++ b/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptSearchResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/transcript_search_response_filters.py b/src/arcmira/types/transcript_search_response_filters.py index 678ebf5..f97ab28 100644 --- a/src/arcmira/types/transcript_search_response_filters.py +++ b/src/arcmira/types/transcript_search_response_filters.py @@ -3,42 +3,22 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata from .named_entity_ref import NamedEntityRef +from .transcript_search_response_filters_kind_item import TranscriptSearchResponseFiltersKindItem class TranscriptSearchResponseFilters(UniversalBaseModel): - channel_ids: typing_extensions.Annotated[ - typing.List[str], - FieldMetadata(alias="channelIds"), - pydantic.Field( - alias="channelIds", description="Channel ids the search was scoped to, after entity_ids were expanded." - ), - ] + channel_ids: typing.List[str] = pydantic.Field() """ Channel ids the search was scoped to, after entity_ids were expanded. """ - entity_ids: typing_extensions.Annotated[ - typing.List[str], - FieldMetadata(alias="entityIds"), - pydantic.Field( - alias="entityIds", - description="Exact explicit entity_ids accepted for this search. Every id was resolved; an unknown id is refused.", - ), - ] + entity_ids: typing.List[str] = pydantic.Field() """ Exact explicit entity_ids accepted for this search. Every id was resolved; an unknown id is refused. """ - published_after: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="publishedAfter"), pydantic.Field(alias="publishedAfter") - ] = None - published_before: typing_extensions.Annotated[ - typing.Optional[str], FieldMetadata(alias="publishedBefore"), pydantic.Field(alias="publishedBefore") - ] = None about: typing.List[NamedEntityRef] = pydantic.Field() """ The about ids, each with its name and type. @@ -49,9 +29,9 @@ class TranscriptSearchResponseFilters(UniversalBaseModel): The by ids, each with its name and type. """ - kind: typing.List[str] = pydantic.Field() + kind: typing.List[TranscriptSearchResponseFiltersKindItem] = pydantic.Field() """ - The kind values applied. + The passage classes applied. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/transcript_search_response_filters_kind_item.py b/src/arcmira/types/transcript_search_response_filters_kind_item.py new file mode 100644 index 0000000..66ef4dc --- /dev/null +++ b/src/arcmira/types/transcript_search_response_filters_kind_item.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptSearchResponseFiltersKindItem = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/types/exposure_meta_free_limit.py b/src/arcmira/types/transcript_search_response_unlock.py similarity index 63% rename from src/arcmira/types/exposure_meta_free_limit.py rename to src/arcmira/types/transcript_search_response_unlock.py index dc9eee2..f688812 100644 --- a/src/arcmira/types/exposure_meta_free_limit.py +++ b/src/arcmira/types/transcript_search_response_unlock.py @@ -6,19 +6,19 @@ from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -class ExposureMetaFreeLimit(UniversalBaseModel): +class TranscriptSearchResponseUnlock(UniversalBaseModel): """ - The anonymous row limits again, under their older key. + Present when your plan's freshness gate cut the window and nothing older matched; note says so. """ - appearances: int = pydantic.Field() + tier: str = pydantic.Field() """ - Same as limits.freeAppearances. + The plan that lifts the freshness gate. """ - entities: int = pydantic.Field() + url: str = pydantic.Field() """ - Same as limits.freeEntitiesPerType. + Where to start that plan. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/video_captions_response.py b/src/arcmira/types/video_captions_response.py deleted file mode 100644 index 8fb8282..0000000 --- a/src/arcmira/types/video_captions_response.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .caption_track import CaptionTrack -from .transcript_video import TranscriptVideo - - -class VideoCaptionsResponse(UniversalBaseModel): - video: TranscriptVideo - languages: typing.List[CaptionTrack] = pydantic.Field() - """ - Every caption track the video offers. Pass a code, or a comma-separated priority list, as language on GET /v1/transcripts/{video_id}. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/video_merge_list_response.py b/src/arcmira/types/video_merge_list_response.py deleted file mode 100644 index 14b092f..0000000 --- a/src/arcmira/types/video_merge_list_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .video_merge_list_response_merges_item import VideoMergeListResponseMergesItem - - -class VideoMergeListResponse(UniversalBaseModel): - merges: typing.List[VideoMergeListResponseMergesItem] = pydantic.Field() - """ - Your pending merges for the video, newest first. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/video_merge_list_response_merges_item.py b/src/arcmira/types/video_merge_list_response_merges_item.py deleted file mode 100644 index 283b29a..0000000 --- a/src/arcmira/types/video_merge_list_response_merges_item.py +++ /dev/null @@ -1,84 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .video_merge_list_response_merges_item_status import VideoMergeListResponseMergesItemStatus - - -class VideoMergeListResponseMergesItem(UniversalBaseModel): - id: int = pydantic.Field() - """ - Pending row id. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - source_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="sourceName"), - pydantic.Field(alias="sourceName", description="The name as it appears in the video."), - ] - """ - The name as it appears in the video. - """ - - target_entity_id: typing_extensions.Annotated[ - int, - FieldMetadata(alias="targetEntityId"), - pydantic.Field(alias="targetEntityId", description="Raw integer entity id the name refers to."), - ] - """ - Raw integer entity id the name refers to. - """ - - target_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="targetName"), - pydantic.Field(alias="targetName", description="Name of the target entity."), - ] - """ - Name of the target entity. - """ - - replace_with: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="replaceWith"), - pydantic.Field(alias="replaceWith", description="Respelling applied to the transcript text. Null when none."), - ] = None - """ - Respelling applied to the transcript text. Null when none. - """ - - status: VideoMergeListResponseMergesItemStatus = pydantic.Field() - """ - Review status. Always pending: only pending rows are listed. - """ - - created_at: typing_extensions.Annotated[ - str, - FieldMetadata(alias="createdAt"), - pydantic.Field(alias="createdAt", description="When the merge was submitted."), - ] - """ - When the merge was submitted. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/video_merge_list_response_merges_item_status.py b/src/arcmira/types/video_merge_list_response_merges_item_status.py deleted file mode 100644 index 0107ea0..0000000 --- a/src/arcmira/types/video_merge_list_response_merges_item_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -VideoMergeListResponseMergesItemStatus = typing.Union[ - typing.Literal["pending", "approved", "rejected", "reverted"], typing.Any -] diff --git a/src/arcmira/types/video_merge_submitted_response.py b/src/arcmira/types/video_merge_submitted_response.py deleted file mode 100644 index 809ebd1..0000000 --- a/src/arcmira/types/video_merge_submitted_response.py +++ /dev/null @@ -1,23 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .video_merge_submitted_response_merge import VideoMergeSubmittedResponseMerge - - -class VideoMergeSubmittedResponse(UniversalBaseModel): - merge: VideoMergeSubmittedResponseMerge = pydantic.Field() - """ - The stored pending merge. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/video_merge_submitted_response_merge.py b/src/arcmira/types/video_merge_submitted_response_merge.py deleted file mode 100644 index 938dcb6..0000000 --- a/src/arcmira/types/video_merge_submitted_response_merge.py +++ /dev/null @@ -1,70 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .video_merge_submitted_response_merge_status import VideoMergeSubmittedResponseMergeStatus - - -class VideoMergeSubmittedResponseMerge(UniversalBaseModel): - """ - The stored pending merge. - """ - - id: int = pydantic.Field() - """ - Pending row id. Withdraw it with DELETE /v1/transcripts/{video_id}/merges/{id}. - """ - - video_id: typing_extensions.Annotated[ - str, - FieldMetadata(alias="videoId"), - pydantic.Field(alias="videoId", description="YouTube video id (11 characters)."), - ] - """ - YouTube video id (11 characters). - """ - - source_name: typing_extensions.Annotated[ - str, - FieldMetadata(alias="sourceName"), - pydantic.Field(alias="sourceName", description="The name as it appears in the video."), - ] - """ - The name as it appears in the video. - """ - - target_entity_id: typing_extensions.Annotated[ - int, - FieldMetadata(alias="targetEntityId"), - pydantic.Field(alias="targetEntityId", description="Raw integer entity id the name refers to."), - ] - """ - Raw integer entity id the name refers to. - """ - - replace_with: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="replaceWith"), - pydantic.Field(alias="replaceWith", description="Respelling applied to the transcript text. Null when none."), - ] = None - """ - Respelling applied to the transcript text. Null when none. - """ - - status: VideoMergeSubmittedResponseMergeStatus = pydantic.Field() - """ - Review status. Always pending on submit. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/video_merge_submitted_response_merge_status.py b/src/arcmira/types/video_merge_submitted_response_merge_status.py deleted file mode 100644 index 700bab9..0000000 --- a/src/arcmira/types/video_merge_submitted_response_merge_status.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -VideoMergeSubmittedResponseMergeStatus = typing.Union[ - typing.Literal["pending", "approved", "rejected", "reverted"], typing.Any -] diff --git a/src/arcmira/types/webhook_secret_rotate_response.py b/src/arcmira/types/webhook_secret_rotate_response.py index d671351..f719562 100644 --- a/src/arcmira/types/webhook_secret_rotate_response.py +++ b/src/arcmira/types/webhook_secret_rotate_response.py @@ -3,44 +3,21 @@ import typing import pydantic -import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata class WebhookSecretRotateResponse(UniversalBaseModel): - webhook_secret: typing_extensions.Annotated[ - str, - FieldMetadata(alias="webhookSecret"), - pydantic.Field( - alias="webhookSecret", - description='The NEW webhook signing secret ("whsec_..."). Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again.', - ), - ] + webhook_secret: str = pydantic.Field() """ The NEW webhook signing secret ("whsec_..."). Store it securely. The same Idempotency-Key can recover it for up to 24 hours while it remains the current or valid previous secret. A displaced or expired secret returns idempotency_result_expired without rotating again. """ - webhook_secret_hint: typing_extensions.Annotated[ - str, - FieldMetadata(alias="webhookSecretHint"), - pydantic.Field( - alias="webhookSecretHint", - description="Last 4 characters of the new secret, for identifying which secret you hold.", - ), - ] + webhook_secret_hint: str = pydantic.Field() """ Last 4 characters of the new secret, for identifying which secret you hold. """ - previous_secret_expires_at: typing_extensions.Annotated[ - typing.Optional[str], - FieldMetadata(alias="previousSecretExpiresAt"), - pydantic.Field( - alias="previousSecretExpiresAt", - description="End of the 24-hour overlap window. Until then, deliveries carry an additional X-Arcmira-Signature-Previous header computed with the previous secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. Null when the monitor had no previous secret (nothing to overlap).", - ), - ] = None + previous_secret_expires_at: typing.Optional[str] = pydantic.Field(default=None) """ End of the 24-hour overlap window. Until then, deliveries carry an additional X-Arcmira-Signature-Previous header computed with the previous secret over the same {timestamp}.{payload} string, so you can verify with either secret while you roll. Null when the monitor had no previous secret (nothing to overlap). """ diff --git a/src/arcmira/types/withdrawn_response.py b/src/arcmira/types/withdrawn_response.py deleted file mode 100644 index 8d4d75a..0000000 --- a/src/arcmira/types/withdrawn_response.py +++ /dev/null @@ -1,22 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class WithdrawnResponse(UniversalBaseModel): - ok: bool = pydantic.Field() - """ - Always true: the pending row was withdrawn. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/wrong_classification_change.py b/src/arcmira/types/wrong_classification_change.py index 13a80cc..d4c7aa0 100644 --- a/src/arcmira/types/wrong_classification_change.py +++ b/src/arcmira/types/wrong_classification_change.py @@ -3,8 +3,10 @@ import typing import pydantic +import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .wrong_classification_change_mention_class import WrongClassificationChangeMentionClass +from ..core.serialization import FieldMetadata +from .wrong_classification_change_class import WrongClassificationChangeClass class WrongClassificationChange(UniversalBaseModel): @@ -12,9 +14,16 @@ class WrongClassificationChange(UniversalBaseModel): For issue_type wrong_classification: the commercial class the row should carry. """ - mention_class: typing.Optional[WrongClassificationChangeMentionClass] = pydantic.Field(default=None) + class_: typing_extensions.Annotated[ + typing.Optional[WrongClassificationChangeClass], + FieldMetadata(alias="class"), + pydantic.Field( + alias="class", + description="The correct commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention).", + ), + ] = None """ - The correct commercial classification. Values: ad_read (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), endorsement (an unpaid personal recommendation), mention (a neutral commercial mention). + The correct commercial class. Values: sponsored (an ad-style promotion heard at that moment: a paid sponsor read, promo code, affiliate plug, thanks for supplied goods or venue, or a show promoting its own product as an ad), organic (an unpaid personal recommendation), mention (a neutral commercial mention). """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/wrong_classification_change_class.py b/src/arcmira/types/wrong_classification_change_class.py new file mode 100644 index 0000000..db3166d --- /dev/null +++ b/src/arcmira/types/wrong_classification_change_class.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +WrongClassificationChangeClass = typing.Union[typing.Literal["sponsored", "organic", "mention"], typing.Any] diff --git a/src/arcmira/types/wrong_classification_change_mention_class.py b/src/arcmira/types/wrong_classification_change_mention_class.py deleted file mode 100644 index 998735f..0000000 --- a/src/arcmira/types/wrong_classification_change_mention_class.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -WrongClassificationChangeMentionClass = typing.Union[typing.Literal["ad_read", "endorsement", "mention"], typing.Any] diff --git a/tests/fixtures/transcription-responses.json b/tests/fixtures/transcription-responses.json index d9724c0..d04aab1 100644 --- a/tests/fixtures/transcription-responses.json +++ b/tests/fixtures/transcription-responses.json @@ -146,7 +146,6 @@ "credits_per_row": 4, "max_on_demand_cents": 0, "on_demand_cents_per_unit": 0.4, - "prepare_url": "/v1/transcriptions", "refund_policy": "Failed generation is refunded. Delivered unlocks are permanent." }, "responses": { @@ -170,31 +169,11 @@ "credits_per_row": 4, "max_on_demand_cents": 0, "on_demand_cents_per_unit": 0.4, - "prepare_url": "/v1/transcriptions", "refund_policy": "Failed generation is refunded. Delivered unlocks are permanent." } } } }, - "get_transcription": { - "status": 200, - "body": { - "id": "00000000-0000-4000-8000-000000000001", - "video_id": "dQw4w9WgXcQ", - "state": "pending", - "status": "queued", - "stage": "queued", - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "eta_seconds": 172, - "next_poll_seconds": 29, - "created_at": "2026-10-01 00:00:00", - "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001" - } - }, "list_transcriptions": { "status": 200, "body": { @@ -221,37 +200,8 @@ "next_cursor": null } }, - "preparation_required": { - "status": 200, - "body": { - "state": "preparation_required", - "quality": "premium", - "video_id": "dQw4w9WgXcQ", - "quote": { - "rows": 300, - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "eligible": true, - "max_on_demand_cents": 0 - }, - "action": { - "method": "POST", - "url": "https://api.arcmira.com/v1/transcriptions", - "body": { - "video_id": "dQw4w9WgXcQ" - } - }, - "last_attempt": { - "status": "refunded", - "error": "Transcription timed out." - } - } - }, "pending_premium": { - "status": 200, + "status": 202, "body": { "state": "pending", "quality": "premium", @@ -274,87 +224,6 @@ } } }, - "submit_pending": { - "status": 200, - "body": { - "job": { - "id": "00000000-0000-4000-8000-000000000001", - "video_id": "dQw4w9WgXcQ", - "state": "pending", - "status": "queued", - "stage": "queued", - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "eta_seconds": 172, - "next_poll_seconds": 29, - "created_at": "2026-10-01 00:00:00", - "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001" - }, - "existing": false - } - }, - "submit_ready": { - "status": 200, - "body": { - "job": { - "id": "00000000-0000-4000-8000-000000000001", - "video_id": "dQw4w9WgXcQ", - "state": "ready", - "status": "complete", - "stage": null, - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "created_at": "2026-10-01 00:00:00", - "completed_at": "2026-10-01 00:04:10", - "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001" - }, - "existing": true - } - }, - "job_ready": { - "status": 200, - "body": { - "id": "00000000-0000-4000-8000-000000000001", - "video_id": "dQw4w9WgXcQ", - "state": "ready", - "status": "complete", - "stage": null, - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "created_at": "2026-10-01 00:00:00", - "completed_at": "2026-10-01 00:04:10", - "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001" - } - }, - "job_refunded": { - "status": 200, - "body": { - "id": "00000000-0000-4000-8000-000000000001", - "video_id": "dQw4w9WgXcQ", - "state": "refunded", - "status": "refunded", - "stage": null, - "charge": { - "unit": "credits", - "amount": 1200, - "from": "included" - }, - "created_at": "2026-10-01 00:00:00", - "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001", - "error": "Transcription timed out.", - "refunded": true, - "completed_at": "2026-10-01 00:04:10" - } - }, "premium_ready": { "status": 200, "body": { @@ -390,7 +259,7 @@ { "id": 0, "name": "John Coogan", - "entity_id": 91, + "entity_id": "ent_91", "confidence": "high" }, { @@ -406,13 +275,51 @@ "note": "Premium transcripts are Arcmira's own. Every Premium transcript is diarized: each line carries a speaker. We identify each speaker from the audio, the video, and internal and community review. We are right most of the time and wrong sometimes, so when a name matters, say it came from Arcmira's speaker identification and cite the line." } }, + "job_ready": { + "status": 200, + "body": { + "id": "00000000-0000-4000-8000-000000000001", + "video_id": "dQw4w9WgXcQ", + "state": "ready", + "status": "complete", + "stage": null, + "charge": { + "unit": "credits", + "amount": 1200, + "from": "included" + }, + "created_at": "2026-10-01 00:00:00", + "completed_at": "2026-10-01 00:04:10", + "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001" + } + }, + "job_refunded": { + "status": 200, + "body": { + "id": "00000000-0000-4000-8000-000000000001", + "video_id": "dQw4w9WgXcQ", + "state": "refunded", + "status": "refunded", + "stage": null, + "charge": { + "unit": "credits", + "amount": 1200, + "from": "included" + }, + "created_at": "2026-10-01 00:00:00", + "status_url": "https://api.arcmira.com/v1/transcriptions/00000000-0000-4000-8000-000000000001", + "error": "Transcription timed out.", + "refunded": true, + "completed_at": "2026-10-01 00:04:10" + } + }, "refused_quota": { "status": 402, "body": { "error": { "type": "quota_exceeded", "code": "quota_exceeded", - "message": "This transcript needs 75 rows and the account has 10 left this month. Raise max_on_demand_cents or upgrade.", + "message": "This transcript needs 75 rows and the account has 10 left this month, past the on-demand budget. Raise the budget in settings or upgrade.", "gate": "rows", "unlock": { "tier": "Pro", @@ -420,17 +327,49 @@ "offer": null }, "doc_url": "https://arcmira.com/docs/errors#quota_exceeded", - "request_id": "req_fixture" - }, - "quote": { - "quarters": 1, - "rows": 75, - "charge": { - "unit": "credits", - "amount": 300, - "from": "mixed" + "request_id": "req_fixture", + "details": { + "quote": { + "quarters": 1, + "rows": 75, + "charge": { + "unit": "credits", + "amount": 300, + "from": "mixed" + }, + "max_on_demand_cents": 104 + } + } + } + } + }, + "paid_plan_required": { + "status": 403, + "body": { + "error": { + "type": "permission_error", + "code": "paid_plan_required", + "message": "Premium transcripts need a paid plan. Captions stay free: read again without quality=premium.", + "gate": "plan", + "unlock": { + "tier": "Pro", + "url": "https://arcmira.com/pricing?src=api", + "offer": null }, - "max_on_demand_cents": 104 + "doc_url": "https://arcmira.com/docs/errors#paid_plan_required", + "request_id": "req_fixture", + "details": { + "quote": { + "quarters": 1, + "rows": 75, + "charge": { + "unit": "credits", + "amount": 300, + "from": "mixed" + }, + "max_on_demand_cents": 104 + } + } } } } diff --git a/tests/test_generation.py b/tests/test_generation.py index d65ea91..85d570b 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -6,38 +6,108 @@ ROOT = Path(__file__).resolve().parents[1] TOOLS = runpy.run_path(str(ROOT / 'scripts/prepare-openapi.py')) +DOCUMENT = json.loads((ROOT / 'fern/openapi.json').read_text()) +NAMES = json.loads((ROOT / 'fern/method-names.json').read_text()) +METHODS = {'get', 'put', 'post', 'patch', 'delete'} + + +def operations(doc): + for path, methods in doc['paths'].items(): + for method, op in methods.items(): + if method in METHODS: + yield path, method, op + + +def body_schema(doc, op): + request = TOOLS['resolve'](doc, op['requestBody']) + return TOOLS['resolve'](doc, request['content']['application/json']['schema']) + class GenerationTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.prepared = TOOLS['prepare'](DOCUMENT, NAMES) + def test_unknown_and_ambiguous_cursor_collections_fail(self): - for props in ({'mystery': {'type':'array'}}, {'requests':{'type':'array'}, 'episodes':{'type':'array'}}): + for props in ({'mystery': {'type': 'array'}}, {'requests': {'type': 'array'}, 'episodes': {'type': 'array'}}): with self.assertRaisesRegex(ValueError, 'Unknown or ambiguous'): TOOLS['collection']({}, {'properties': props}) - def test_actual_contract_generates_union_key_and_collections(self): - doc = json.loads((ROOT / 'fern/openapi.json').read_text()) - before = copy.deepcopy(doc) - prepared = TOOLS['prepare'](doc, json.loads((ROOT / 'fern/method-names.json').read_text())) - self.assertEqual(doc, before) - union = prepared['components']['schemas']['TranscriptResult'] + def test_prepare_leaves_the_input_document_unchanged(self): + before = copy.deepcopy(DOCUMENT) + TOOLS['prepare'](DOCUMENT, NAMES) + self.assertEqual(DOCUMENT, before) + + def test_transcript_result_discriminates_ready_and_pending(self): + union = self.prepared['components']['schemas']['TranscriptResult'] self.assertEqual(union['discriminator']['propertyName'], 'state') - self.assertEqual(set(union['discriminator']['mapping']), {'ready','preparation_required','pending'}) - for path, collection in [('/v1/transcriptions','requests'),('/v1/channels/{channel_id}/videos','episodes')]: - self.assertEqual(prepared['paths'][path]['get']['x-fern-pagination']['results'], '$response.'+collection) - post = prepared['paths']['/v1/transcriptions']['post'] - self.assertFalse(next(p for p in post['parameters'] if p['name']=='Idempotency-Key').get('required')) - body = post['requestBody']['content']['application/json']['schema'] - self.assertNotIn('videoId', body['properties']) - self.assertNotIn('max_rows', body.get('required', [])) - self.assertEqual(post['x-fern-sdk-method-name'], 'request') + self.assertEqual(set(union['discriminator']['mapping']), {'ready', 'pending'}) + responses = self.prepared['paths']['/v1/transcripts/{video_id}']['get']['responses'] + for code in ('200', '202'): + self.assertEqual(responses[code]['content']['application/json']['schema'], {'$ref': '#/components/schemas/TranscriptResult'}) + + def test_cursor_pagination_reads_the_named_collection(self): + expected = { + '/v1/mentions': 'mentions', + '/v1/recommendations': 'recommendations', + '/v1/channels/{channel_id}/videos': 'episodes', + '/v1/transcriptions': 'requests', + } + paged = {path: op['x-fern-pagination']['results'] for path, _, op in operations(self.prepared) if 'x-fern-pagination' in op} + self.assertEqual(paged, {path: '$response.' + items for path, items in expected.items()}) + for path in expected: + pagination = self.prepared['paths'][path]['get']['x-fern-pagination'] + self.assertEqual((pagination['cursor'], pagination['next_cursor']), ('$request.cursor', '$response.next_cursor')) + + def test_every_operation_gets_its_name_from_method_names(self): + for path, method, op in operations(self.prepared): + name = NAMES[op['operationId']] + self.assertEqual((op['x-fern-sdk-group-name'], op['x-fern-sdk-method-name']), (name['group'], name['method']), f'{method} {path}') + search = self.prepared['paths']['/v1/search']['get'] + self.assertEqual((search['x-fern-sdk-group-name'], search['x-fern-sdk-method-name']), (['transcripts'], 'search')) + + def test_an_operation_without_a_name_fails(self): + names = {key: value for key, value in NAMES.items() if key != 'list_mentions'} + with self.assertRaisesRegex(ValueError, 'names no SDK method for: list_mentions'): + TOOLS['prepare'](DOCUMENT, names) + + def test_a_name_for_a_missing_operation_fails(self): + names = {**NAMES, 'submit_transcription': {'group': ['transcripts'], 'method': 'request'}} + with self.assertRaisesRegex(ValueError, 'no longer has: submit_transcription'): + TOOLS['prepare'](DOCUMENT, names) + doc = copy.deepcopy(DOCUMENT) + del doc['paths']['/v1/integrations/slack'] + with self.assertRaisesRegex(ValueError, 'no longer has: list_slack_integrations'): + TOOLS['prepare'](doc, NAMES) + + def test_excluded_operations_are_absent(self): + present = {op['operationId'] for _, _, op in operations(DOCUMENT)} + self.assertLessEqual(TOOLS['EXCLUDED'], present) + prepared = {op['operationId'] for _, _, op in operations(self.prepared)} + self.assertFalse(TOOLS['EXCLUDED'] & prepared) + self.assertEqual(prepared, set(NAMES)) + for path in ('/v1/openapi.json', '/v1/signups', '/v1/signups/verify'): + self.assertNotIn(path, self.prepared['paths']) + + def test_feedback_body_requires_type_and_query_is_body_only(self): + op = self.prepared['paths']['/v1/feedback']['post'] + self.assertIn('type', body_schema(self.prepared, op)['required']) + self.assertFalse([p['name'] for p in op['parameters'] if p.get('in') == 'query' and p['name'] in {'type', 'query'}]) + + def test_transcription_purchase_post_is_gone(self): + self.assertEqual(set(self.prepared['paths']['/v1/transcriptions']), {'get'}) + self.assertIn('TranscriptJob', self.prepared['components']['schemas']) + self.assertNotIn('TranscriptionJob', self.prepared['components']['schemas']) + + def test_resolve_suggestion_stays_nullable(self): + suggestion = self.prepared['components']['schemas']['ResolveSuggestion'] + self.assertEqual(suggestion['type'], ['object', 'null']) + self.assertNotIn('allOf', suggestion) def test_installed_api_error_matches_the_preserved_override(self): self.assertEqual((ROOT / 'src/arcmira/core/api_error.py').read_text(), (ROOT / 'scripts/overrides/api_error.py').read_text()) self.assertIn('overrides/api_error.py', (ROOT / 'scripts/install-generated.py').read_text()) - def test_installed_prepare_and_wait_matches_the_preserved_override(self): - self.assertEqual((ROOT / 'src/arcmira/transcripts/prepare.py').read_text(), (ROOT / 'scripts/overrides/prepare.py').read_text()) - client = (ROOT / 'src/arcmira/transcripts/client.py').read_text() - self.assertIn('class TranscriptsClient(PrepareAndWait):', client) - self.assertIn('class AsyncTranscriptsClient(AsyncPrepareAndWait):', client) -if __name__ == '__main__': unittest.main() +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_prepare.py b/tests/test_prepare.py deleted file mode 100644 index 72853d3..0000000 --- a/tests/test_prepare.py +++ /dev/null @@ -1,225 +0,0 @@ -import asyncio -import json -import threading -import types -import unittest -from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from pathlib import Path -from unittest import mock -from urllib.parse import parse_qs, urlparse - -from arcmira import ( - Arcmira, - AsyncArcmira, - PreparationFailedError, - PreparationTimeoutError, - PremiumUnavailableError, -) -from arcmira.core.api_error import ApiError -from arcmira.transcripts import prepare -from arcmira.types.transcript_result import TranscriptResult_Ready - -FIXTURES = json.loads((Path(__file__).parent / 'fixtures/transcription-responses.json').read_text()) -body = lambda name: FIXTURES[name]['body'] -VIDEO = 'dQw4w9WgXcQ' -JOB = body('get_transcription')['id'] -READ = f'/v1/transcripts/{VIDEO}' -SUBMIT = '/v1/transcriptions' -POLL = f'/v1/transcriptions/{JOB}' -PRICE_REFUSED = {'error': {'type': 'permission_error', 'code': 'spend_limit_exceeded', 'message': 'The spend limit would be exceeded.', 'doc_url': 'https://arcmira.com/docs/errors', 'request_id': 'fixture-request'}} - - -def reply(name, status=200, retry_after=None): - return status, retry_after, body(name) - - -class Handler(BaseHTTPRequestHandler): - script = {} - calls = [] - - def log_message(self, *args): - pass - - def do_GET(self): - self.answer() - - def do_POST(self): - self.answer() - - def answer(self): - path = urlparse(self.path).path - payload = self.rfile.read(int(self.headers.get('Content-Length', 0))).decode() - Handler.calls.append((self.command, path, parse_qs(urlparse(self.path).query), dict(self.headers), payload)) - queue = Handler.script[(self.command, path)] - status, retry_after, result = queue.pop(0) if len(queue) > 1 else queue[0] - self.send_response(status) - self.send_header('Content-Type', 'application/json') - if retry_after is not None: - self.send_header('Retry-After', str(retry_after)) - self.end_headers() - self.wfile.write(json.dumps(result).encode()) - - -class FakeClock: - def __init__(self): - self.now = 0.0 - self.slept = [] - - def monotonic(self): - return self.now - - def sleep(self, seconds): - self.slept.append(seconds) - self.now += seconds - - -class PrepareAndWaitTests(unittest.TestCase): - @classmethod - def setUpClass(cls): - cls.server = ThreadingHTTPServer(('127.0.0.1', 0), Handler) - cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True) - cls.thread.start() - cls.base = f'http://127.0.0.1:{cls.server.server_port}' - cls.client = Arcmira(api_key='local-test-key', base_url=cls.base, max_retries=0, timeout=5) - - @classmethod - def tearDownClass(cls): - cls.server.shutdown() - cls.server.server_close() - cls.thread.join() - - def setUp(self): - Handler.calls = [] - self.clock = FakeClock() - patches = [ - mock.patch.object(prepare, 'time', self.clock), - mock.patch.object(prepare, 'asyncio', types.SimpleNamespace(sleep=self.async_sleep)), - ] - for patch in patches: - patch.start() - self.addCleanup(patch.stop) - - async def async_sleep(self, seconds): - self.clock.sleep(seconds) - - def script(self, **routes): - Handler.script = { - ('GET', READ): routes['read'], - ('POST', SUBMIT): routes.get('submit', [reply('submit_pending', 202)]), - ('GET', POLL): routes.get('poll', [reply('job_ready')]), - } - - def requests(self, method, path): - return [call for call in Handler.calls if call[0] == method and call[1] == path] - - def assert_zero_dollar_post(self): - (_, _, _, headers, payload), = self.requests('POST', SUBMIT) - self.assertEqual(json.loads(payload), {'video_id': VIDEO}) - self.assertNotIn('Idempotency-Key', headers) - - def test_ready_returns_the_owned_transcript_without_posting(self): - self.script(read=[reply('premium_ready')]) - transcript = self.client.transcripts.prepare_and_wait(VIDEO) - self.assertIsInstance(transcript, TranscriptResult_Ready) - self.assertEqual((transcript.quality, transcript.rows_billed), ('premium', 0)) - self.assertEqual([line.speaker for line in transcript.lines], [0, 1]) - self.assertEqual(self.requests('GET', READ)[0][2], {'quality': ['premium']}) - self.assertEqual(self.requests('POST', SUBMIT), []) - self.assertEqual(self.clock.slept, []) - - def test_preparation_required_posts_once_polls_and_reads_the_transcript(self): - self.script( - read=[reply('preparation_required'), reply('premium_ready')], - poll=[reply('get_transcription', retry_after=3), reply('job_ready')], - ) - transcript = self.client.transcripts.prepare_and_wait(VIDEO) - self.assertEqual(transcript.quality, 'premium') - self.assert_zero_dollar_post() - self.assertEqual(len(self.requests('GET', POLL)), 2) - self.assertEqual(len(self.requests('GET', READ)), 2) - self.assertEqual(self.clock.slept, [29, 3]) - - def test_a_pending_read_polls_the_job_without_posting(self): - self.script( - read=[reply('pending_premium', 202, retry_after=5), reply('premium_ready')], - poll=[reply('job_ready')], - ) - self.assertEqual(self.client.transcripts.prepare_and_wait(VIDEO).quality, 'premium') - self.assertEqual(self.requests('POST', SUBMIT), []) - self.assertEqual(self.clock.slept, [5]) - - def test_a_ready_submission_skips_polling(self): - self.script(read=[reply('preparation_required'), reply('premium_ready')], submit=[reply('submit_ready')]) - self.client.transcripts.prepare_and_wait(VIDEO) - self.assertEqual(self.requests('GET', POLL), []) - self.assertEqual(self.clock.slept, []) - - def test_money_sends_the_quoted_max_rows_and_one_idempotency_key(self): - self.script(read=[reply('preparation_required'), reply('premium_ready')]) - self.client.transcripts.prepare_and_wait(VIDEO, max_on_demand_cents=25) - (_, _, _, headers, payload), = self.requests('POST', SUBMIT) - self.assertEqual(json.loads(payload), {'video_id': VIDEO, 'max_rows': 300, 'max_on_demand_cents': 25}) - self.assertRegex(headers['Idempotency-Key'], r'^[0-9a-f-]{36}$') - - def test_a_pending_job_that_outlasts_the_timeout_raises_with_the_job(self): - self.script(read=[reply('preparation_required')], poll=[reply('get_transcription')]) - with self.assertRaises(PreparationTimeoutError) as caught: - self.client.transcripts.prepare_and_wait(VIDEO, timeout_seconds=40) - self.assertEqual(caught.exception.job.id, JOB) - self.assertEqual(caught.exception.job.state, 'pending') - self.assertEqual(self.clock.slept, [29, 11]) - - def test_a_refunded_job_raises_and_never_reposts(self): - self.script(read=[reply('preparation_required')], poll=[reply('job_refunded')]) - with self.assertRaises(PreparationFailedError) as caught: - self.client.transcripts.prepare_and_wait(VIDEO) - self.assertEqual((caught.exception.job.state, caught.exception.job.error), ('refunded', 'Transcription timed out.')) - self.assertEqual(len(self.requests('POST', SUBMIT)), 1) - - def test_captions_are_never_returned_for_a_premium_ask(self): - self.script(read=[reply('get_transcript')]) - with self.assertRaises(PremiumUnavailableError) as caught: - self.client.transcripts.prepare_and_wait(VIDEO) - self.assertEqual(caught.exception.transcript.quality, 'captions') - - def test_an_api_refusal_surfaces_as_api_error(self): - self.script(read=[reply('preparation_required')], submit=[(402, None, PRICE_REFUSED)]) - with self.assertRaises(ApiError) as caught: - self.client.transcripts.prepare_and_wait(VIDEO, max_on_demand_cents=25) - self.assertEqual(str(caught.exception), '402 spend_limit_exceeded: The spend limit would be exceeded.') - - def test_negative_cents_are_refused_before_any_request(self): - with self.assertRaises(ValueError): - self.client.transcripts.prepare_and_wait(VIDEO, max_on_demand_cents=-1) - self.assertEqual(Handler.calls, []) - - def test_async_client_follows_the_same_plan(self): - async def run(): - client = AsyncArcmira(api_key='local-test-key', base_url=self.base, max_retries=0, timeout=5) - self.script( - read=[reply('preparation_required'), reply('premium_ready')], - poll=[reply('get_transcription', retry_after=3), reply('job_ready')], - ) - transcript = await client.transcripts.prepare_and_wait(VIDEO) - self.assertEqual(transcript.quality, 'premium') - self.assert_zero_dollar_post() - self.assertEqual(self.clock.slept, [29, 3]) - - self.script(read=[reply('preparation_required')], poll=[reply('get_transcription')]) - Handler.calls.clear() - self.clock.slept.clear() - with self.assertRaises(PreparationTimeoutError) as caught: - await client.transcripts.prepare_and_wait(VIDEO, timeout_seconds=40) - self.assertEqual(caught.exception.job.id, JOB) - - self.script(read=[reply('preparation_required'), reply('premium_ready')]) - Handler.calls.clear() - await client.transcripts.prepare_and_wait(VIDEO, max_on_demand_cents=25) - (_, _, _, headers, payload), = self.requests('POST', SUBMIT) - self.assertEqual(json.loads(payload)['max_rows'], 300) - self.assertIn('Idempotency-Key', headers) - asyncio.run(run()) - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_transcription.py b/tests/test_transcription.py index 8eec3b7..6dd73bb 100644 --- a/tests/test_transcription.py +++ b/tests/test_transcription.py @@ -9,21 +9,81 @@ from arcmira import Arcmira, AsyncArcmira from arcmira.core.api_error import ApiError -from arcmira.errors import PaymentRequiredError -from arcmira.types.transcript_result import TranscriptResult_Ready, TranscriptResult_Pending +from arcmira.errors import ForbiddenError, PaymentRequiredError +from arcmira.types.transcript_result import TranscriptResult_Pending, TranscriptResult_Ready FIXTURES = json.loads((Path(__file__).parent / 'fixtures/transcription-responses.json').read_text()) -QUOTE = FIXTURES['quote_transcription']['body'] -REQUEST = FIXTURES['get_transcription']['body'] +PENDING = FIXTURES['pending_premium']['body'] REFUSED = FIXTURES['refused_quota']['body'] -def error(code, kind): - return dict(type=kind, code=code, message=code, doc_url='https://arcmira.com/docs/errors', request_id='fixture-request') +PAID_PLAN = FIXTURES['paid_plan_required']['body'] +QUOTE = FIXTURES['quote_transcription']['body'] +REQUEST = FIXTURES['list_transcriptions']['body']['requests'][0] +REFUNDED = {**FIXTURES['job_refunded']['body'], 'title': None} PAGE_CAP = 5 CURSOR = 'signed+/opaque==&cursor' -PENDING = FIXTURES['pending_premium']['body'] +ENTITY = dict(id='ent_14', canonical_id='ent_14', name='Ramp', type='organization', is_canonical=True) +ENTITY_REF = dict(id='ent_14', name='Ramp', type='organization') +WINDOW = dict(after=None, before=None) CALLS = [] -RECEIPTS = {} + + +def page(query, first, second): + continued = 'cursor' in query + return [second if continued else first], dict(has_more=not continued, next_cursor=None if continued else CURSOR) + + +def mention(video_id): + return dict(id='men_' + video_id, entity=ENTITY_REF, media=dict(video_id=video_id), is_appearance=False, sentiment='neutral', start_seconds=0, end_seconds=20) + + +def tracker(body): + return dict(id='trk_1', entity_name=body['entity_name'], entity_type=body['entity_type'], display_name=body['entity_name'], notify_email=True, notify_webhook=False, notify_slack=False, paused=False, created_at='2026-10-02T00:00:00Z', email_delivery_count=0, webhook_delivery_count=0, slack_delivery_count=0) + + +def monitor(id, body): + return dict(id=id, name='Fixture', paused=body.get('paused', False), notify_emails=[], notify_webhook=False, notify_slack=False, created_at='2026-10-01T00:00:00Z', updated_at='2026-10-02T00:00:00Z', access='account', muted=False, tracker_count=1) + + +def transcript(video_id, query): + if video_id == 'pending0000': + return 202, PENDING + if video_id == 'quota000000': + return 402, REFUSED + if video_id == 'plan0000000': + return 403, PAID_PLAN + if query.get('quality') == ['premium']: + return 200, FIXTURES['premium_ready']['body'] + return 200, FIXTURES['get_transcript']['body'] + + +def route(method, path, query, body): + parts = path.strip('/').split('/') + if method == 'GET' and path.endswith('/quote'): + return 200, QUOTE + if method == 'GET' and parts[:2] == ['v1', 'transcripts']: + return transcript(parts[2], query) + if method == 'GET' and path == '/v1/entities/resolve': + best = dict(ENTITY_REF, match='exact') + return 200, dict(query=query['q'][0], context=None, confidence='exact', best=best, suggested=None, ask=None, candidates=[best], note='Fixture') + if method == 'GET' and path == '/v1/transcriptions': + requests, more = page(query, REQUEST, REFUNDED) + return 200, dict(requests=requests, **more) + if method == 'GET' and path.endswith('/videos'): + episodes, more = page(query, *[dict(video_id=v, channel_id='UC-test', watch_url='https://arcmira.com/video/' + v) for v in ('video-1', 'video-2')]) + return 200, dict(channel=dict(youtube_channel_id='UC-test', name='Fixture'), episodes=episodes, returned=1, window=WINDOW, note='Fixture', **more) + if method == 'GET' and path == '/v1/mentions': + mentions, more = page(query, mention('video-1'), mention('video-2')) + return 200, dict(mentions=mentions, entity=ENTITY, window=WINDOW, **more) + if method == 'GET' and path == '/v1/recommendations': + row = dict(id='com_1', entity=ENTITY_REF, media=dict(video_id='video-1'), confidence=0.9, speaker_role='host', start_seconds=10, end_seconds=40, **{'class': 'sponsored'}) + return 200, dict(recommendations=[row], entity=ENTITY, window=WINDOW, has_more=False, next_cursor=None) + if method == 'POST' and path == '/v1/trackers': + return 201, dict(tracker=tracker(body), message='Tracker created.') + if method == 'PATCH' and parts[:2] == ['v1', 'monitors']: + return 200, dict(monitor=monitor(parts[2], body), message='Monitor updated.') + return 404, dict(error=dict(type='not_found', code='not_found', message=f'No fixture for {method} {path}', doc_url='https://arcmira.com/docs/errors', request_id='fixture')) + class Handler(BaseHTTPRequestHandler): def log_message(self, *args): @@ -35,40 +95,28 @@ def do_GET(self): def do_POST(self): self.answer() + def do_PATCH(self): + self.answer() + def answer(self): url = urlparse(self.path) query = parse_qs(url.query) - body = self.rfile.read(int(self.headers.get('Content-Length', 0))).decode() - CALLS.append((url.path, query, dict(self.headers), body, self.command)) - status, extra = 200, {} - if url.path.endswith('/quote'): - result = QUOTE - elif url.path == '/v1/transcriptions' and self.command == 'POST' and 'refused0000' in body: - status, result = 402, REFUSED - elif url.path == '/v1/transcriptions' and self.command == 'POST': - key = self.headers['Idempotency-Key'] - replay = key in RECEIPTS - if replay and RECEIPTS[key] != body: - status, result = 409, {'error': error('idempotency_conflict', 'conflict_error')} - else: - RECEIPTS[key] = body - status = 200 if replay else 202 - result = {'job': REQUEST, 'existing': replay} - if replay: extra['Idempotency-Replayed'] = 'true' - elif url.path == '/v1/transcriptions': - result = {'requests': [{**REQUEST, 'id': 'request-2' if 'cursor' in query else 'request-1'}], 'has_more': 'cursor' not in query, 'next_cursor': None if 'cursor' in query else CURSOR} - elif '/channels/' in url.path: - episode = dict(video_id='video-2' if 'cursor' in query else 'video-1', channel_id='UC-test', watch_url='https://arcmira.com/video/test') - result = dict(channel={'youtube_channel_id':'UC-test','name':'Fixture'}, episodes=[episode], returned=1, has_more='cursor' not in query, next_cursor=None if 'cursor' in query else CURSOR, indexed_through=None, index_age_days=None, as_of=None, note='Fixture') - elif url.path.endswith('/pending0000'): - status, result = 202, PENDING - else: - result = FIXTURES['get_transcript']['body'] + raw = self.rfile.read(int(self.headers.get('Content-Length', 0))).decode() + body = json.loads(raw) if raw else {} + CALLS.append(dict(method=self.command, path=url.path, query=query, body=body)) + status, result = route(self.command, url.path, query, body) self.send_response(status) - for name, value in {'Content-Type': 'application/json', 'Retry-After': '5', **extra}.items(): self.send_header(name, value) + self.send_header('Content-Type', 'application/json') + if status == 202: + self.send_header('Retry-After', str(result['job']['next_poll_seconds'])) self.end_headers() self.wfile.write(json.dumps(result).encode()) + +def capped(pager): + return list(itertools.islice(pager, PAGE_CAP)) + + class GeneratedClientTests(unittest.TestCase): @classmethod def setUpClass(cls): @@ -84,69 +132,126 @@ def tearDownClass(cls): cls.server.server_close() cls.thread.join() - def test_ready_pending_status_and_union(self): - ready = self.client.transcripts.with_raw_response.get(video_id='dQw4w9WgXcQ') - self.assertIsInstance(ready.data, TranscriptResult_Ready) + def calls_since(self, before, path): + return [call for call in CALLS[before:] if call['path'] == path] + + def test_ready_read(self): + ready = self.client.transcripts.with_raw_response.get('dQw4w9WgXcQ') self.assertEqual(ready.status_code, 200) - self.assertTrue(ready.data.lines) - pending = self.client.transcripts.with_raw_response.get(video_id='pending0000', quality='premium') - self.assertIsInstance(pending.data, TranscriptResult_Pending) + self.assertIsInstance(ready.data, TranscriptResult_Ready) + self.assertEqual(ready.data.quality, 'captions') + self.assertEqual(ready.data.lines[0].text, FIXTURES['get_transcript']['body']['lines'][0]['text']) + premium = self.client.transcripts.get('dQw4w9WgXcQ', quality='premium') + self.assertIsInstance(premium, TranscriptResult_Ready) + self.assertEqual(premium.quality, 'premium') + self.assertEqual(premium.revision, FIXTURES['premium_ready']['body']['revision']) + + def test_premium_pending_carries_the_job_and_retry_after(self): + before = len(CALLS) + pending = self.client.transcripts.with_raw_response.get('pending0000', quality='premium') self.assertEqual(pending.status_code, 202) - self.assertEqual(pending.data.job.status_url, PENDING['job']['status_url']) - self.assertEqual(pending.headers['retry-after'], '5') + self.assertIsInstance(pending.data, TranscriptResult_Pending) + self.assertEqual(pending.data.job.id, PENDING['job']['id']) + self.assertEqual(pending.data.job.next_poll_seconds, PENDING['job']['next_poll_seconds']) + self.assertEqual(pending.headers['retry-after'], str(PENDING['job']['next_poll_seconds'])) + sent = self.calls_since(before, '/v1/transcripts/pending0000') + self.assertEqual([(call['method'], call['query']) for call in sent], [('GET', {'quality': ['premium']})]) + + def test_premium_refusals_are_typed_errors_with_the_quote(self): + for video_id, error_type, fixture in (('quota000000', PaymentRequiredError, REFUSED), ('plan0000000', ForbiddenError, PAID_PLAN)): + with self.subTest(code=fixture['error']['code']): + with self.assertRaises(error_type) as caught: + self.client.transcripts.get(video_id, quality='premium') + error = caught.exception.body.error + expected = fixture['error'] + self.assertIsInstance(caught.exception, ApiError) + self.assertEqual((error.type, error.code, error.gate), (expected['type'], expected['code'], expected['gate'])) + self.assertEqual(error.unlock.tier, expected['unlock']['tier']) + quote = error.details.quote + self.assertEqual(quote.rows, expected['details']['quote']['rows']) + self.assertEqual(quote.charge.amount, expected['details']['quote']['charge']['amount']) + self.assertEqual(quote.max_on_demand_cents, expected['details']['quote']['max_on_demand_cents']) + self.assertEqual(str(caught.exception), f"{caught.exception.status_code} {expected['code']}: {expected['message']}") - def test_quote_and_refusal(self): - quote = self.client.transcripts.quote(video_id='dQw4w9WgXcQ') + def test_resolve_with_best_and_no_suggestion(self): + before = len(CALLS) + match = self.client.entities.resolve(q='Ramp', type='organization') + self.assertEqual(self.calls_since(before, '/v1/entities/resolve')[0]['query'], {'q': ['Ramp'], 'type': ['organization']}) + self.assertEqual(match.best.id, 'ent_14') + self.assertIsNone(match.suggested) + + def test_quote_passes_through(self): + quote = self.client.transcripts.quote('dQw4w9WgXcQ') self.assertEqual(quote.quote.rows, QUOTE['quote']['rows']) - with self.assertRaises(PaymentRequiredError) as caught: - self.client.transcripts.request(video_id='refused0000') - refusal = caught.exception.body - self.assertEqual(refusal.error.code, 'quota_exceeded') - self.assertEqual(refusal.error.unlock.tier, REFUSED['error']['unlock']['tier']) - self.assertEqual(refusal.quote.rows, REFUSED['quote']['rows']) - self.assertEqual(refusal.quote.charge.amount, REFUSED['quote']['charge']['amount']) - self.assertEqual(refusal.quote.max_on_demand_cents, REFUSED['quote']['max_on_demand_cents']) - self.assertEqual(str(caught.exception), f"402 quota_exceeded: {REFUSED['error']['message']}") - self.assertNotIn('headers', str(caught.exception)) - - def test_preparation_exact_intent_and_replay(self): - intent = dict(video_id='dQw4w9WgXcQ', max_rows=300, max_on_demand_cents=0, idempotency_key='python-saved-intent') - first = self.client.transcripts.with_raw_response.request(**intent) - replay = self.client.transcripts.with_raw_response.request(**intent) - sent = [call[2]['Idempotency-Key'] for call in CALLS if call[0] == '/v1/transcriptions' and call[4] == 'POST' and 'Idempotency-Key' in call[2]] - self.assertEqual(sent[-2:], ['python-saved-intent', 'python-saved-intent']) - self.assertEqual(first.status_code, 202) - self.assertEqual(replay.status_code, 200) - self.assertEqual(replay.data.job.id, first.data.job.id) - self.assertTrue(replay.data.existing) - self.assertEqual(replay.headers['idempotency-replayed'], 'true') - self.assertEqual(json.loads(CALLS[-1][3]), dict(video_id='dQw4w9WgXcQ', max_rows=300, max_on_demand_cents=0)) - with self.assertRaises(ApiError) as caught: - self.client.transcripts.request(**{**intent, 'max_rows':600}) - self.assertEqual(caught.exception.status_code, 409) - - def test_request_and_episode_arrays_preserve_opaque_cursor(self): + self.assertEqual(quote.charge.amount, QUOTE['charge']['amount']) + self.assertEqual(quote.max_on_demand_cents, QUOTE['max_on_demand_cents']) + + def test_pagers_follow_the_opaque_cursor(self): + cases = ( + ('/v1/transcriptions', lambda: self.client.transcripts.list_requests(limit=1), lambda row: row.id, [REQUEST['id'], REFUNDED['id']]), + ('/v1/channels/UC-test/videos', lambda: self.client.channels.videos.list('UC-test', limit=1), lambda row: row.video_id, ['video-1', 'video-2']), + ('/v1/mentions', lambda: self.client.mentions.list(entity_id='ent_14', limit=1), lambda row: row.media.video_id, ['video-1', 'video-2']), + ) + for path, pager, key, expected in cases: + with self.subTest(path=path): + before = len(CALLS) + self.assertEqual([key(row) for row in capped(pager())], expected) + continued = [call['query'] for call in self.calls_since(before, path) if 'cursor' in call['query']] + self.assertEqual(len(continued), 1) + self.assertEqual(continued[0]['cursor'], [CURSOR]) + self.assertEqual(continued[0]['limit'], ['1']) + + def test_refunded_request_row_parses(self): + rows = capped(self.client.transcripts.list_requests(limit=1)) + self.assertEqual([row.state for row in rows], ['pending', 'refunded']) + + def test_mentions_send_entity_id_and_the_half_open_window(self): + before = len(CALLS) + rows = capped(self.client.mentions.list(entity_id='ent_14', channel_id='UC-test', after='2026-09-01', before='2026-09-02')) + self.assertEqual(len(rows), 2) + first = self.calls_since(before, '/v1/mentions')[0]['query'] + self.assertEqual(first, {'entity_id': ['ent_14'], 'channel_id': ['UC-test'], 'after': ['2026-09-01'], 'before': ['2026-09-02']}) + + def test_recommendations_send_class(self): + before = len(CALLS) + rows = capped(self.client.recommendations.list(entity_id='ent_14', class_='sponsored')) + self.assertEqual([row.class_ for row in rows], ['sponsored']) + query = self.calls_since(before, '/v1/recommendations')[0]['query'] + self.assertEqual(query, {'entity_id': ['ent_14'], 'class': ['sponsored']}) + + def test_trackers_create_sends_entity_name_and_type(self): + before = len(CALLS) + created = self.client.trackers.create(entity_name='Ramp', entity_type='organization') + self.assertEqual(self.calls_since(before, '/v1/trackers')[0]['body'], {'entity_name': 'Ramp', 'entity_type': 'organization'}) + self.assertEqual(created.tracker.entity_name, 'Ramp') + self.assertFalse(created.tracker.paused) + + def test_monitors_update_sends_paused(self): before = len(CALLS) - capped = lambda pager: list(itertools.islice(pager, PAGE_CAP)) - self.assertEqual([x.id for x in capped(self.client.transcripts.list_requests(limit=1))], ['request-1','request-2']) - self.assertEqual([x.video_id for x in capped(self.client.channels.videos.list(channel_id='UC-test', limit=1))], ['video-1','video-2']) - continued = [call for call in CALLS[before:] if 'cursor' in call[1]] - self.assertEqual(len(continued), 2) - for call in continued: - self.assertEqual(call[1]['cursor'], [CURSOR]) - self.assertEqual(call[1]['limit'], ['1']) - - def test_async_pending_and_pagination(self): + updated = self.client.monitors.update('mon_1', paused=True) + self.assertEqual(self.calls_since(before, '/v1/monitors/mon_1')[0]['body'], {'paused': True}) + self.assertTrue(updated.monitor.paused) + + def test_async_read_and_pagination(self): async def run(): client = AsyncArcmira(api_key='local-test-key', base_url=self.base, max_retries=0, timeout=5) - pending = await client.transcripts.with_raw_response.get(video_id='pending0000', quality='premium') + ready = await client.transcripts.get('dQw4w9WgXcQ') + self.assertIsInstance(ready, TranscriptResult_Ready) + pending = await client.transcripts.with_raw_response.get('pending0000', quality='premium') self.assertEqual(pending.status_code, 202) self.assertIsInstance(pending.data, TranscriptResult_Pending) + self.assertEqual(pending.headers['retry-after'], str(PENDING['job']['next_poll_seconds'])) + with self.assertRaises(PaymentRequiredError) as caught: + await client.transcripts.get('quota000000', quality='premium') + self.assertEqual(caught.exception.body.error.details.quote.rows, REFUSED['error']['details']['quote']['rows']) rows = [] - async for row in await client.transcripts.list_requests(limit=1): - rows.append(row.id) - if len(rows) >= PAGE_CAP: break - self.assertEqual(rows, ['request-1','request-2']) + async for row in await client.mentions.list(entity_id='ent_14', limit=1): + rows.append(row.media.video_id) + if len(rows) >= PAGE_CAP: + break + self.assertEqual(rows, ['video-1', 'video-2']) asyncio.run(run()) -if __name__ == '__main__': unittest.main() + +if __name__ == '__main__': + unittest.main() From 377218a9222e9255683f4fda92406ade425342b8 Mon Sep 17 00:00:00 2001 From: Zeal Caiden Date: Fri, 2 Oct 2026 20:53:12 -0700 Subject: [PATCH 2/4] Regenerate from the final v1 document: failed Premium state, retry, names[], org A Premium read whose last purchase failed answers state failed with last_attempt and buys nothing; retry=True buys again. monitors.entities.add takes names, trackers accept org, and priced refusals share one RefusedQuote type. The README loop stops on failed. --- CHANGELOG.md | 5 +- README.md | 4 +- fern/openapi.json | 580 +++++++----------- reference.md | 35 +- src/arcmira/__init__.py | 90 +-- src/arcmira/monitors/__init__.py | 13 +- src/arcmira/monitors/alerts/client.py | 4 +- src/arcmira/monitors/alerts/raw_client.py | 4 +- src/arcmira/monitors/entities/__init__.py | 21 +- src/arcmira/monitors/entities/client.py | 37 +- src/arcmira/monitors/entities/raw_client.py | 36 +- .../monitors/entities/types/__init__.py | 15 +- .../types/add_entities_request_names_item.py | 34 + ...es_request_names_item_person_match_mode.py | 5 + .../add_entities_request_names_item_type.py | 7 + src/arcmira/trackers/alerts/client.py | 4 +- src/arcmira/trackers/alerts/raw_client.py | 4 +- src/arcmira/trackers/client.py | 4 +- src/arcmira/trackers/raw_client.py | 4 +- .../create_trackers_request_entity_type.py | 2 +- src/arcmira/transcripts/client.py | 22 +- src/arcmira/transcripts/raw_client.py | 22 +- src/arcmira/types/__init__.py | 108 ++-- .../types/channel_sponsors_response_access.py | 10 - ...hannel_sponsors_response_access_details.py | 13 +- ..._sponsors_response_access_details_quote.py | 33 - ...rs_response_access_details_quote_charge.py | 40 -- ...sponse_access_details_quote_charge_from.py | 7 - ...sponse_access_details_quote_charge_unit.py | 5 - .../types/entity_momentum_response_access.py | 10 - ...entity_momentum_response_access_details.py | 13 +- ..._momentum_response_access_details_quote.py | 33 - ...um_response_access_details_quote_charge.py | 40 -- ...sponse_access_details_quote_charge_from.py | 7 - ...sponse_access_details_quote_charge_unit.py | 5 - src/arcmira/types/error_error.py | 10 - src/arcmira/types/error_error_details.py | 13 +- .../error_error_details_quote_charge_from.py | 5 - .../error_error_details_quote_charge_unit.py | 5 - .../types/monitor_add_entities_response.py | 2 +- src/arcmira/types/monitor_entity_result.py | 17 +- .../types/monitor_entity_result_type.py | 7 + ...rror_details_quote.py => refused_quote.py} | 10 +- ...uote_charge.py => refused_quote_charge.py} | 10 +- .../types/refused_quote_charge_from.py | 5 + .../types/refused_quote_charge_unit.py | 5 + src/arcmira/types/transcript_failed.py | 33 + .../types/transcript_failed_last_attempt.py | 32 + .../transcript_failed_last_attempt_status.py | 5 + .../types/transcript_failed_quality.py | 5 + src/arcmira/types/transcript_job.py | 6 +- src/arcmira/types/transcript_response.py | 2 +- .../types/transcript_response_access.py | 10 - .../transcript_response_access_details.py | 13 +- ...ranscript_response_access_details_quote.py | 33 - ...pt_response_access_details_quote_charge.py | 36 -- ...sponse_access_details_quote_charge_from.py | 7 - ...sponse_access_details_quote_charge_unit.py | 5 - .../types/transcript_response_lines_item.py | 2 +- src/arcmira/types/transcript_result.py | 23 +- .../transcript_search_response_access.py | 10 - ...anscript_search_response_access_details.py | 13 +- ...pt_search_response_access_details_quote.py | 33 - ...ch_response_access_details_quote_charge.py | 40 -- ...sponse_access_details_quote_charge_from.py | 7 - ...sponse_access_details_quote_charge_unit.py | 5 - tests/test_generation.py | 4 +- 67 files changed, 652 insertions(+), 1037 deletions(-) create mode 100644 src/arcmira/monitors/entities/types/add_entities_request_names_item.py create mode 100644 src/arcmira/monitors/entities/types/add_entities_request_names_item_person_match_mode.py create mode 100644 src/arcmira/monitors/entities/types/add_entities_request_names_item_type.py delete mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote.py delete mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py delete mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py delete mode 100644 src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote.py delete mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge.py delete mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py delete mode 100644 src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/error_error_details_quote_charge_from.py delete mode 100644 src/arcmira/types/error_error_details_quote_charge_unit.py create mode 100644 src/arcmira/types/monitor_entity_result_type.py rename src/arcmira/types/{error_error_details_quote.py => refused_quote.py} (58%) rename src/arcmira/types/{error_error_details_quote_charge.py => refused_quote_charge.py} (75%) create mode 100644 src/arcmira/types/refused_quote_charge_from.py create mode 100644 src/arcmira/types/refused_quote_charge_unit.py create mode 100644 src/arcmira/types/transcript_failed.py create mode 100644 src/arcmira/types/transcript_failed_last_attempt.py create mode 100644 src/arcmira/types/transcript_failed_last_attempt_status.py create mode 100644 src/arcmira/types/transcript_failed_quality.py delete mode 100644 src/arcmira/types/transcript_response_access_details_quote.py delete mode 100644 src/arcmira/types/transcript_response_access_details_quote_charge.py delete mode 100644 src/arcmira/types/transcript_response_access_details_quote_charge_from.py delete mode 100644 src/arcmira/types/transcript_response_access_details_quote_charge_unit.py delete mode 100644 src/arcmira/types/transcript_search_response_access_details_quote.py delete mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge.py delete mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py delete mode 100644 src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4a79c..a394538 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,8 @@ Generated from the v1 document of 2026-10-02. The document dropped from 88 opera Added. -- `client.monitors.entities.add(id, entity_ids=[...], person_match_mode=None)` follows entities by id in a monitor. It reuses the account's tracker for each entity or creates one, then attaches it. Each id gets one result, and an id that cannot be followed comes back with `attached: false` and a reason. +- `client.monitors.entities.add(id, entity_ids=None, names=None, person_match_mode=None)` follows entities in a monitor by id, or by exact name and type for a name not yet indexed. It reuses the account's tracker for each entity or creates one, then attaches it. Each id or name gets one result, and one that cannot be followed comes back with `attached: false` and a reason. +- `trackers.create` and `names` accept `org` for `organization`, and the duplicate check ignores case. - `client.integrations.slack.list()` lists the account's active Slack workspaces with the `slack_integration_id` and `slack_channel_id` values a monitor needs for Slack delivery. Slack is connected in the dashboard, not through the API. - `monitors.create` takes `team_id`. `Monitor` carries `access`, `muted` and `team`. - `feedback.submit` takes `category` and `mcp_call_id`, and `type="experience"` reports how a task went as a whole. @@ -14,7 +15,7 @@ Added. Breaking changes from 0.3. -- Premium is one read. `transcripts.get(video_id, quality="premium")` answers `ready` (200) when the account owns the transcript. Otherwise it buys the whole video within the plan and the account's on-demand budget and answers `pending` (202) with the `job` and a `Retry-After` header. Read again after `Retry-After`. Repeated reads join the same purchase and never buy twice. `TranscriptResult` is now `TranscriptResult_Ready | TranscriptResult_Pending`. The `preparation_required` state and `TranscriptResult_PreparationRequired` are gone. +- Premium is one read. `transcripts.get(video_id, quality="premium")` answers `ready` (200) when the account owns the transcript. Otherwise it buys the whole video within the plan and the account's on-demand budget and answers `pending` (202) with the `job` and a `Retry-After` header. Read again after `Retry-After`. Repeated reads join the same purchase and never buy twice. When the last purchase for the video failed or was refunded, the read answers `failed` (200) with the `job` and `last_attempt` and buys nothing; pass `retry=True` to buy it again. `TranscriptResult` is now `TranscriptResult_Ready | TranscriptResult_Pending | TranscriptResult_Failed`. Priced refusals carry one `RefusedQuote` type. The `preparation_required` state and `TranscriptResult_PreparationRequired` are gone. - `transcripts.prepare_and_wait` is removed, along with `PreparationError`, `PreparationFailedError`, `PreparationTimeoutError` and `PremiumUnavailableError`. Loop on `transcripts.get(..., quality="premium")` until `state == "ready"`. The README has the loop. - `transcripts.request` and `transcripts.status` are removed, with the `TranscriptRequestSubmitResponse` type. The Premium read buys and reports its own job. `transcripts.list_requests` still lists past purchases. - A Premium refusal raises from the read itself. `PaymentRequiredError` (402) carries `quota_exceeded` or `spend_limit_exceeded`, and `ForbiddenError` (403) carries `paid_plan_required`. Nothing is charged. diff --git a/README.md b/README.md index e39a165..907a2da 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,12 @@ for _ in range(60): for line in read.data.lines: print(line.start, line.text) break + if read.data.state == "failed": + raise RuntimeError(f"{read.data.last_attempt.status}: {read.data.last_attempt.error}") time.sleep(int(read.headers.get("retry-after") or read.data.job.next_poll_seconds or 10)) ``` -`read.data` is a `TranscriptResult`, discriminated on `state`. `ready` carries the transcript. `pending` carries `job`, with `eta_seconds`, `next_poll_seconds` and `charge`. Without `with_raw_response`, `client.transcripts.get(...)` returns the same union without the status and headers. +`read.data` is a `TranscriptResult`, discriminated on `state`. `ready` carries the transcript. `pending` carries `job`, with `eta_seconds`, `next_poll_seconds` and `charge`. `failed` means the last purchase failed or was refunded; it carries `job` and `last_attempt`, buys nothing, and `retry=True` buys it again. Without `with_raw_response`, `client.transcripts.get(...)` returns the same union without the status and headers. A quote is free and changes nothing. diff --git a/fern/openapi.json b/fern/openapi.json index f724757..d3d0ba2 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -319,67 +319,11 @@ "type": "integer", "description": "Present on rate gates. Mirrors the Retry-After header." }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, "details": { "type": "object", "properties": { "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + "$ref": "#/components/schemas/RefusedQuote" }, "existing_id": { "type": "string", @@ -413,11 +357,6 @@ "type": "not_found", "description": "A referenced alert row does not exist or belongs to another account." }, - { - "code": "anchor_mismatch", - "type": "conflict_error", - "description": "The anchored lines no longer hash to anchor.contentHash. current_revision is the revision to re-read; send a re-anchored correction under a new key." - }, { "code": "api_not_enabled", "type": "permission_error", @@ -464,7 +403,7 @@ { "code": "forbidden", "type": "permission_error", - "description": "The entity page list refused the request." + "description": "The caller may not perform this operation on this resource." }, { "code": "freshness_requires_paid", @@ -493,11 +432,6 @@ "gate": "key", "description": "The key lacks the scope this route needs." }, - { - "code": "internal_error", - "type": "server_error", - "description": "The entity page list failed. Retry with backoff." - }, { "code": "invalid_api_key", "type": "authentication_error", @@ -550,26 +484,6 @@ "gate": "key", "description": "Transcription jobs belong to an account; sign up for a key." }, - { - "code": "list_unavailable", - "type": "server_error", - "description": "The entity page list failed with a status other than 400, 403, 404 or 500. Retry with backoff." - }, - { - "code": "max_charge_exceeded", - "type": "conflict_error", - "description": "The whole-video price or on-demand charge exceeds the caller-approved ceiling. Missing max_on_demand_cents authorizes zero monetary overage." - }, - { - "code": "max_rows_exceeded", - "type": "conflict_error", - "description": "The current whole-video quote exceeds the maximum authorized rows. Review the quote before submitting a new intent." - }, - { - "code": "method_not_allowed", - "type": "invalid_request_error", - "description": "The route accepts another HTTP method." - }, { "code": "monitor_creation_unavailable", "type": "server_error", @@ -617,11 +531,6 @@ "gate": "exposure_law", "description": "Premium transcript text needs a plan with Premium transcripts." }, - { - "code": "purchase_authority_changed", - "type": "conflict_error", - "description": "The account billing authority (plan or on-demand controls) changed after the intent was accepted. Nothing was charged. Review the quote and send a new intent." - }, { "code": "quota_exceeded", "type": "quota_exceeded", @@ -650,11 +559,6 @@ "type": "conflict_error", "description": "The operation conflicts with existing resource state." }, - { - "code": "revision_mismatch", - "type": "conflict_error", - "description": "The transcript changed after the revision the correction echoed. current_revision is the revision to re-read; send a re-anchored correction under a new key." - }, { "code": "scope_too_broad", "type": "invalid_request_error", @@ -665,11 +569,6 @@ "type": "server_error", "description": "Transcript search is unavailable (HTTP 503). Retry-After carries the wait." }, - { - "code": "sequence_mismatch", - "type": "conflict_error", - "description": "HTTP 412. The correction seq is not the next one for this video. expected_seq is the next seq; rebase and resend under the same key." - }, { "code": "server_error", "type": "server_error", @@ -685,11 +584,6 @@ "type": "rate_limit_error", "description": "Verification code sends hit a per-address, per-IP, or per-client cap. Retry-After carries the wait." }, - { - "code": "speaker_not_correctable", - "type": "conflict_error", - "description": "The speaker shares a voice track or was named in review, so a correction cannot land on their lines alone." - }, { "code": "spend_limit_exceeded", "type": "quota_exceeded", @@ -1015,6 +909,53 @@ ], "description": "The value the boundary withheld, not the reason it refused. kind is a closed vocabulary and the fields beside it are fixed per kind; see https://arcmira.com/docs/errors#resource-kinds." }, + "RefusedQuote": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptQuote" + }, + { + "type": "object", + "properties": { + "charge": { + "type": "object", + "properties": { + "unit": { + "type": "string", + "enum": [ + "rows", + "credits" + ] + }, + "amount": { + "type": "number" + }, + "from": { + "type": "string", + "enum": [ + "included", + "on_demand", + "mixed" + ], + "description": "Where the charge would come from at the current balance." + } + }, + "required": [ + "unit", + "amount", + "from" + ], + "description": "What the purchase would charge at the current balance. Absent when no current price could be read." + }, + "max_on_demand_cents": { + "type": "integer", + "description": "The on-demand money, in whole cents, this purchase needs beyond included credits at the current balance." + } + } + } + ], + "description": "The refused price, on a priced refusal: quota_exceeded, spend_limit_exceeded and paid_plan_required." + }, "TranscriptQuote": { "type": "object", "properties": { @@ -3115,67 +3056,11 @@ "type": "integer", "description": "Present on rate gates. Mirrors the Retry-After header." }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, "details": { "type": "object", "properties": { "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + "$ref": "#/components/schemas/RefusedQuote" }, "existing_id": { "type": "string", @@ -3514,67 +3399,11 @@ "type": "integer", "description": "Present on rate gates. Mirrors the Retry-After header." }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, "details": { "type": "object", "properties": { "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + "$ref": "#/components/schemas/RefusedQuote" }, "existing_id": { "type": "string", @@ -4076,67 +3905,11 @@ "type": "integer", "description": "Present on rate gates. Mirrors the Retry-After header." }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, "details": { "type": "object", "properties": { "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + "$ref": "#/components/schemas/RefusedQuote" }, "existing_id": { "type": "string", @@ -5537,7 +5310,7 @@ "items": { "$ref": "#/components/schemas/MonitorEntityResult" }, - "description": "One result per distinct requested entity id, in request order." + "description": "One result per distinct requested entity id, then one per distinct requested name, each in request order." } }, "required": [ @@ -5550,7 +5323,22 @@ "properties": { "entity_id": { "type": "string", - "description": "The entity id as requested." + "description": "The entity id as requested. Present on an entity_ids result." + }, + "name": { + "type": "string", + "description": "The name as requested. Present on a names result." + }, + "type": { + "type": "string", + "enum": [ + "person", + "organization", + "product", + "topic", + "channel" + ], + "description": "The type as requested, org read as organization. Present on a names result." }, "canonical_entity_id": { "type": "string", @@ -5579,7 +5367,7 @@ "tracker_limit_reached", "tracked_in_another_monitor" ], - "description": "Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants)." + "description": "Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants). A names result can only carry tracker_limit_reached or tracked_in_another_monitor." }, "current_monitor_id": { "type": "string", @@ -5587,7 +5375,6 @@ } }, "required": [ - "entity_id", "tracker_id", "created", "attached" @@ -5892,7 +5679,7 @@ }, "index": { "type": "integer", - "description": "Line index, present on every Premium line. Echo it as anchor.segmentIndex when you correct the line." + "description": "Line index, present on every Premium line. Stable within one revision." } }, "required": [ @@ -5966,7 +5753,7 @@ }, "revision": { "type": "string", - "description": "Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. Echo it on every correction; a 409 means it changed underneath you, so read again." + "description": "Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. It changes when any of those change." }, "range": { "type": "object", @@ -6102,67 +5889,11 @@ "type": "integer", "description": "Present on rate gates. Mirrors the Retry-After header." }, - "current_revision": { - "type": "string", - "description": "On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction." - }, - "expected_seq": { - "type": "integer", - "description": "On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key." - }, "details": { "type": "object", "properties": { "quote": { - "allOf": [ - { - "$ref": "#/components/schemas/TranscriptQuote" - }, - { - "type": "object", - "properties": { - "charge": { - "type": "object", - "properties": { - "unit": { - "type": "string", - "enum": [ - "rows", - "credits" - ] - }, - "amount": { - "type": "number" - }, - "from": { - "type": "string", - "enum": [ - "included", - "on_demand", - "mixed" - ], - "description": "Where the charge would come from at the current balance." - } - }, - "required": [ - "unit", - "amount", - "from" - ], - "description": "What the purchase would charge at the current balance. Absent when no current price could be read." - }, - "max_on_demand_cents": { - "type": "integer", - "description": "The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent." - } - } - } - ], - "description": "The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required." - }, - "existing_request_id": { - "type": "string", - "description": "On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another." + "$ref": "#/components/schemas/RefusedQuote" }, "existing_id": { "type": "string", @@ -6459,11 +6190,11 @@ }, "error": { "type": "string", - "description": "Failure reason. Only present when state is failed or refunded." + "description": "Failure reason. Only present when state is failed or refunded, or status is refund_pending." }, "refunded": { "type": "boolean", - "description": "True when the charge was returned. Only present when state is failed or refunded." + "description": "True when the charge was returned. Only present when state is failed or refunded, or status is refund_pending (false until the refund lands)." }, "created_at": { "type": "string", @@ -6475,7 +6206,7 @@ }, "status_url": { "type": "string", - "description": "Absolute URL of GET /v1/transcriptions/{id} for this job." + "description": "Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it prepares, 200 ready once it is, and 200 failed if it failed." } }, "required": [ @@ -6489,6 +6220,71 @@ ], "description": "Your open Premium purchase for this video, when captions were served while it prepares." }, + "TranscriptFailed": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "failed" + ] + }, + "quality": { + "type": "string", + "enum": [ + "premium" + ] + }, + "video_id": { + "type": "string" + }, + "job": { + "allOf": [ + { + "$ref": "#/components/schemas/TranscriptionJob" + }, + { + "description": "The last Premium purchase for this video. Its state is failed or refunded, or its status is refund_pending while the refund settles." + } + ] + }, + "last_attempt": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "failed", + "refund_pending", + "refunded" + ], + "description": "How the last purchase ended: failed, refunded (the charge was returned), or refund_pending (the refund is still settling)." + }, + "error": { + "type": "string", + "description": "Why it failed." + } + }, + "required": [ + "status", + "error" + ], + "description": "The failed purchase in brief: job.status and job.error." + }, + "note": { + "type": "string", + "description": "What to do next: read again with retry=true to buy the video again, or wait while the refund settles." + } + }, + "required": [ + "state", + "quality", + "video_id", + "job", + "last_attempt", + "note" + ] + }, "TranscriptPending": { "type": "object", "properties": { @@ -6513,7 +6309,7 @@ "$ref": "#/components/schemas/TranscriptionJob" }, { - "description": "The Premium purchase this read started or joined. Read this transcript again after Retry-After; job.status_url polls the same purchase." + "description": "The Premium purchase this read started or joined. Read this transcript again after Retry-After (job.status_url is that read); later reads join the same purchase." } ] } @@ -9762,7 +9558,7 @@ ], "operationId": "list_monitor_alerts", "summary": "List recent monitor alerts", - "description": "The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", + "description": "The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", "security": [ { "bearerAuth": [] @@ -9847,8 +9643,8 @@ "Monitors" ], "operationId": "add_monitor_entities", - "summary": "Follow entities in a monitor by id", - "description": "Follows each entity ({ entity_ids: [\"ent_...\"] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes.", + "summary": "Follow entities in a monitor by id or exact name", + "description": "Follows each entity ({ entity_ids: [\"ent_...\"] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes.", "security": [ { "bearerAuth": [] @@ -9888,9 +9684,47 @@ "type": "string", "pattern": "^ent_[1-9][0-9]*$" }, - "minItems": 1, - "maxItems": 90, - "description": "Entity ids (\"ent_...\") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity." + "description": "Entity ids (\"ent_...\") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity." + }, + "names": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "The exact name to watch, matched case-insensitively against analyzed media, so it can be followed before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel." + }, + "type": { + "type": "string", + "enum": [ + "person", + "organization", + "org", + "product", + "topic", + "channel" + ], + "description": "person, organization (org is accepted), product, topic or channel." + }, + "person_match_mode": { + "type": "string", + "enum": [ + "mentions", + "appearances", + "both" + ], + "description": "For a person tracker this request creates; defaults to the top-level person_match_mode. Other types ignore it." + } + }, + "required": [ + "name", + "type" + ], + "additionalProperties": false + }, + "description": "Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once." }, "person_match_mode": { "type": "string", @@ -9902,9 +9736,6 @@ "description": "For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id})." } }, - "required": [ - "entity_ids" - ], "additionalProperties": false } } @@ -10136,13 +9967,14 @@ "entity_name": { "type": "string", "minLength": 1, - "description": "The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id." + "description": "The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id." }, "entity_type": { "type": "string", "enum": [ "person", "organization", + "org", "product", "topic", "channel" @@ -10574,7 +10406,7 @@ ], "operationId": "list_tracker_alerts", "summary": "List recent tracker alerts", - "description": "The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", + "description": "The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id (\"ent_{n}\") and mention_id (\"men_{n}\") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert.", "security": [ { "bearerAuth": [] @@ -10660,7 +10492,7 @@ ], "operationId": "get_transcript", "summary": "Get a video transcript", - "description": "Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections.", + "description": "Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision.", "security": [ { "bearerAuth": [] @@ -10684,10 +10516,10 @@ "captions", "premium" ], - "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings." + "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings." }, "required": false, - "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.", + "description": "captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.", "name": "quality", "in": "query" }, @@ -10739,6 +10571,16 @@ "name": "end", "in": "query" }, + { + "schema": { + "type": "boolean", + "description": "Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing." + }, + "required": false, + "description": "Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing.", + "name": "retry", + "in": "query" + }, { "schema": { "type": "boolean", @@ -10765,7 +10607,7 @@ ], "responses": { "200": { - "description": "state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note).", + "description": "state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last purchase for this video failed; job and last_attempt say why, nothing was bought, and retry=true buys again.", "headers": { "X-Request-Id": { "$ref": "#/components/headers/X-Request-Id" @@ -10786,7 +10628,21 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TranscriptResponse" + "oneOf": [ + { + "$ref": "#/components/schemas/TranscriptResponse" + }, + { + "$ref": "#/components/schemas/TranscriptFailed" + } + ], + "discriminator": { + "propertyName": "state", + "mapping": { + "ready": "#/components/schemas/TranscriptResponse", + "failed": "#/components/schemas/TranscriptFailed" + } + } } } } diff --git a/reference.md b/reference.md index ed1c9b3..2f8c711 100644 --- a/reference.md +++ b/reference.md @@ -1244,7 +1244,7 @@ client.transcripts.search(
-Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. +Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision.
@@ -1293,7 +1293,7 @@ client.transcripts.get(
-**quality:** `typing.Optional[GetTranscriptsRequestQuality]` — captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. +**quality:** `typing.Optional[GetTranscriptsRequestQuality]` — captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings.
@@ -1333,6 +1333,14 @@ client.transcripts.get(
+**retry:** `typing.Optional[bool]` — Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing. + +
+
+ +
+
+ **refresh:** `typing.Optional[bool]` — Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query.
@@ -2266,7 +2274,7 @@ client.trackers.create(
-**entity_name:** `str` — The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. +**entity_name:** `str` — The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id.
@@ -3052,7 +3060,7 @@ client.monitors.trackers.add(
-The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. +The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert.
@@ -3134,7 +3142,7 @@ client.monitors.alerts.list(
-Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. +Follows each entity ({ entity_ids: ["ent_..."] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes.
@@ -3160,9 +3168,6 @@ client = Arcmira( client.monitors.entities.add( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_ids=[ - "entity_ids" - ], ) ``` @@ -3187,7 +3192,7 @@ client.monitors.entities.add(
-**entity_ids:** `typing.List[str]` — Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. +**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation.
@@ -3195,7 +3200,15 @@ client.monitors.entities.add(
-**idempotency_key:** `typing.Optional[str]` — One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. +**entity_ids:** `typing.Optional[typing.List[str]]` — Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity. + +
+
+ +
+
+ +**names:** `typing.Optional[typing.List[AddEntitiesRequestNamesItem]]` — Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once.
@@ -3236,7 +3249,7 @@ client.monitors.entities.add(
-The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. +The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert.
diff --git a/src/arcmira/__init__.py b/src/arcmira/__init__.py index feb8597..c0fcf84 100644 --- a/src/arcmira/__init__.py +++ b/src/arcmira/__init__.py @@ -23,10 +23,6 @@ ChannelSponsorsResponse, ChannelSponsorsResponseAccess, ChannelSponsorsResponseAccessDetails, - ChannelSponsorsResponseAccessDetailsQuote, - ChannelSponsorsResponseAccessDetailsQuoteCharge, - ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, - ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, ChannelSponsorsResponseAccessGate, ChannelSponsorsResponseAccessReason, ChannelSponsorsResponseAccessType, @@ -45,10 +41,6 @@ EntityMomentumResponse, EntityMomentumResponseAccess, EntityMomentumResponseAccessDetails, - EntityMomentumResponseAccessDetailsQuote, - EntityMomentumResponseAccessDetailsQuoteCharge, - EntityMomentumResponseAccessDetailsQuoteChargeFrom, - EntityMomentumResponseAccessDetailsQuoteChargeUnit, EntityMomentumResponseAccessGate, EntityMomentumResponseAccessReason, EntityMomentumResponseAccessType, @@ -66,10 +58,6 @@ Error, ErrorError, ErrorErrorDetails, - ErrorErrorDetailsQuote, - ErrorErrorDetailsQuoteCharge, - ErrorErrorDetailsQuoteChargeFrom, - ErrorErrorDetailsQuoteChargeUnit, ErrorErrorGate, ErrorErrorReason, ErrorErrorType, @@ -153,6 +141,7 @@ MonitorEmailRecipientsItemStatus, MonitorEntityResult, MonitorEntityResultReason, + MonitorEntityResultType, MonitorListResponse, MonitorListResponseMonitorsItem, MonitorListResponseMonitorsItemSlackIntegration, @@ -174,6 +163,10 @@ RecommendationListResponse, RecommendationMedia, RecommendationMediaSourceChannel, + RefusedQuote, + RefusedQuoteCharge, + RefusedQuoteChargeFrom, + RefusedQuoteChargeUnit, ResolveCandidate, ResolveCandidateMatch, ResolveSuggestion, @@ -190,6 +183,10 @@ Tracker, TrackerListResponse, TrackerMutationResponse, + TranscriptFailed, + TranscriptFailedLastAttempt, + TranscriptFailedLastAttemptStatus, + TranscriptFailedQuality, TranscriptJob, TranscriptJobCharge, TranscriptJobChargeFrom, @@ -211,10 +208,6 @@ TranscriptResponse, TranscriptResponseAccess, TranscriptResponseAccessDetails, - TranscriptResponseAccessDetailsQuote, - TranscriptResponseAccessDetailsQuoteCharge, - TranscriptResponseAccessDetailsQuoteChargeFrom, - TranscriptResponseAccessDetailsQuoteChargeUnit, TranscriptResponseAccessGate, TranscriptResponseAccessReason, TranscriptResponseAccessType, @@ -227,16 +220,13 @@ TranscriptResponseSource, TranscriptResponseSpeakersItem, TranscriptResult, + TranscriptResult_Failed, TranscriptResult_Pending, TranscriptResult_Ready, TranscriptSearchChunk, TranscriptSearchResponse, TranscriptSearchResponseAccess, TranscriptSearchResponseAccessDetails, - TranscriptSearchResponseAccessDetailsQuote, - TranscriptSearchResponseAccessDetailsQuoteCharge, - TranscriptSearchResponseAccessDetailsQuoteChargeFrom, - TranscriptSearchResponseAccessDetailsQuoteChargeUnit, TranscriptSearchResponseAccessGate, TranscriptSearchResponseAccessReason, TranscriptSearchResponseAccessType, @@ -326,10 +316,6 @@ "ChannelSponsorsResponse": ".types", "ChannelSponsorsResponseAccess": ".types", "ChannelSponsorsResponseAccessDetails": ".types", - "ChannelSponsorsResponseAccessDetailsQuote": ".types", - "ChannelSponsorsResponseAccessDetailsQuoteCharge": ".types", - "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom": ".types", - "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit": ".types", "ChannelSponsorsResponseAccessGate": ".types", "ChannelSponsorsResponseAccessReason": ".types", "ChannelSponsorsResponseAccessType": ".types", @@ -355,10 +341,6 @@ "EntityMomentumResponse": ".types", "EntityMomentumResponseAccess": ".types", "EntityMomentumResponseAccessDetails": ".types", - "EntityMomentumResponseAccessDetailsQuote": ".types", - "EntityMomentumResponseAccessDetailsQuoteCharge": ".types", - "EntityMomentumResponseAccessDetailsQuoteChargeFrom": ".types", - "EntityMomentumResponseAccessDetailsQuoteChargeUnit": ".types", "EntityMomentumResponseAccessGate": ".types", "EntityMomentumResponseAccessReason": ".types", "EntityMomentumResponseAccessType": ".types", @@ -376,10 +358,6 @@ "Error": ".types", "ErrorError": ".types", "ErrorErrorDetails": ".types", - "ErrorErrorDetailsQuote": ".types", - "ErrorErrorDetailsQuoteCharge": ".types", - "ErrorErrorDetailsQuoteChargeFrom": ".types", - "ErrorErrorDetailsQuoteChargeUnit": ".types", "ErrorErrorGate": ".types", "ErrorErrorReason": ".types", "ErrorErrorType": ".types", @@ -469,6 +447,7 @@ "MonitorEmailRecipientsItemStatus": ".types", "MonitorEntityResult": ".types", "MonitorEntityResultReason": ".types", + "MonitorEntityResultType": ".types", "MonitorListResponse": ".types", "MonitorListResponseMonitorsItem": ".types", "MonitorListResponseMonitorsItemSlackIntegration": ".types", @@ -492,6 +471,10 @@ "RecommendationListResponse": ".types", "RecommendationMedia": ".types", "RecommendationMediaSourceChannel": ".types", + "RefusedQuote": ".types", + "RefusedQuoteCharge": ".types", + "RefusedQuoteChargeFrom": ".types", + "RefusedQuoteChargeUnit": ".types", "ResolveCandidate": ".types", "ResolveCandidateMatch": ".types", "ResolveEntitiesRequestType": ".entities", @@ -520,6 +503,10 @@ "Tracker": ".types", "TrackerListResponse": ".types", "TrackerMutationResponse": ".types", + "TranscriptFailed": ".types", + "TranscriptFailedLastAttempt": ".types", + "TranscriptFailedLastAttemptStatus": ".types", + "TranscriptFailedQuality": ".types", "TranscriptJob": ".types", "TranscriptJobCharge": ".types", "TranscriptJobChargeFrom": ".types", @@ -541,10 +528,6 @@ "TranscriptResponse": ".types", "TranscriptResponseAccess": ".types", "TranscriptResponseAccessDetails": ".types", - "TranscriptResponseAccessDetailsQuote": ".types", - "TranscriptResponseAccessDetailsQuoteCharge": ".types", - "TranscriptResponseAccessDetailsQuoteChargeFrom": ".types", - "TranscriptResponseAccessDetailsQuoteChargeUnit": ".types", "TranscriptResponseAccessGate": ".types", "TranscriptResponseAccessReason": ".types", "TranscriptResponseAccessType": ".types", @@ -557,16 +540,13 @@ "TranscriptResponseSource": ".types", "TranscriptResponseSpeakersItem": ".types", "TranscriptResult": ".types", + "TranscriptResult_Failed": ".types", "TranscriptResult_Pending": ".types", "TranscriptResult_Ready": ".types", "TranscriptSearchChunk": ".types", "TranscriptSearchResponse": ".types", "TranscriptSearchResponseAccess": ".types", "TranscriptSearchResponseAccessDetails": ".types", - "TranscriptSearchResponseAccessDetailsQuote": ".types", - "TranscriptSearchResponseAccessDetailsQuoteCharge": ".types", - "TranscriptSearchResponseAccessDetailsQuoteChargeFrom": ".types", - "TranscriptSearchResponseAccessDetailsQuoteChargeUnit": ".types", "TranscriptSearchResponseAccessGate": ".types", "TranscriptSearchResponseAccessReason": ".types", "TranscriptSearchResponseAccessType": ".types", @@ -647,10 +627,6 @@ def __dir__(): "ChannelSponsorsResponse", "ChannelSponsorsResponseAccess", "ChannelSponsorsResponseAccessDetails", - "ChannelSponsorsResponseAccessDetailsQuote", - "ChannelSponsorsResponseAccessDetailsQuoteCharge", - "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom", - "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit", "ChannelSponsorsResponseAccessGate", "ChannelSponsorsResponseAccessReason", "ChannelSponsorsResponseAccessType", @@ -676,10 +652,6 @@ def __dir__(): "EntityMomentumResponse", "EntityMomentumResponseAccess", "EntityMomentumResponseAccessDetails", - "EntityMomentumResponseAccessDetailsQuote", - "EntityMomentumResponseAccessDetailsQuoteCharge", - "EntityMomentumResponseAccessDetailsQuoteChargeFrom", - "EntityMomentumResponseAccessDetailsQuoteChargeUnit", "EntityMomentumResponseAccessGate", "EntityMomentumResponseAccessReason", "EntityMomentumResponseAccessType", @@ -697,10 +669,6 @@ def __dir__(): "Error", "ErrorError", "ErrorErrorDetails", - "ErrorErrorDetailsQuote", - "ErrorErrorDetailsQuoteCharge", - "ErrorErrorDetailsQuoteChargeFrom", - "ErrorErrorDetailsQuoteChargeUnit", "ErrorErrorGate", "ErrorErrorReason", "ErrorErrorType", @@ -790,6 +758,7 @@ def __dir__(): "MonitorEmailRecipientsItemStatus", "MonitorEntityResult", "MonitorEntityResultReason", + "MonitorEntityResultType", "MonitorListResponse", "MonitorListResponseMonitorsItem", "MonitorListResponseMonitorsItemSlackIntegration", @@ -813,6 +782,10 @@ def __dir__(): "RecommendationListResponse", "RecommendationMedia", "RecommendationMediaSourceChannel", + "RefusedQuote", + "RefusedQuoteCharge", + "RefusedQuoteChargeFrom", + "RefusedQuoteChargeUnit", "ResolveCandidate", "ResolveCandidateMatch", "ResolveEntitiesRequestType", @@ -841,6 +814,10 @@ def __dir__(): "Tracker", "TrackerListResponse", "TrackerMutationResponse", + "TranscriptFailed", + "TranscriptFailedLastAttempt", + "TranscriptFailedLastAttemptStatus", + "TranscriptFailedQuality", "TranscriptJob", "TranscriptJobCharge", "TranscriptJobChargeFrom", @@ -862,10 +839,6 @@ def __dir__(): "TranscriptResponse", "TranscriptResponseAccess", "TranscriptResponseAccessDetails", - "TranscriptResponseAccessDetailsQuote", - "TranscriptResponseAccessDetailsQuoteCharge", - "TranscriptResponseAccessDetailsQuoteChargeFrom", - "TranscriptResponseAccessDetailsQuoteChargeUnit", "TranscriptResponseAccessGate", "TranscriptResponseAccessReason", "TranscriptResponseAccessType", @@ -878,16 +851,13 @@ def __dir__(): "TranscriptResponseSource", "TranscriptResponseSpeakersItem", "TranscriptResult", + "TranscriptResult_Failed", "TranscriptResult_Pending", "TranscriptResult_Ready", "TranscriptSearchChunk", "TranscriptSearchResponse", "TranscriptSearchResponseAccess", "TranscriptSearchResponseAccessDetails", - "TranscriptSearchResponseAccessDetailsQuote", - "TranscriptSearchResponseAccessDetailsQuoteCharge", - "TranscriptSearchResponseAccessDetailsQuoteChargeFrom", - "TranscriptSearchResponseAccessDetailsQuoteChargeUnit", "TranscriptSearchResponseAccessGate", "TranscriptSearchResponseAccessReason", "TranscriptSearchResponseAccessType", diff --git a/src/arcmira/monitors/__init__.py b/src/arcmira/monitors/__init__.py index f3a8782..86d9142 100644 --- a/src/arcmira/monitors/__init__.py +++ b/src/arcmira/monitors/__init__.py @@ -8,8 +8,16 @@ if typing.TYPE_CHECKING: from .types import CreateMonitorsRequestNotifyFrequency, UpdateMonitorsRequestNotifyFrequency from . import alerts, entities, trackers - from .entities import AddEntitiesRequestPersonMatchMode + from .entities import ( + AddEntitiesRequestNamesItem, + AddEntitiesRequestNamesItemPersonMatchMode, + AddEntitiesRequestNamesItemType, + AddEntitiesRequestPersonMatchMode, + ) _dynamic_imports: typing.Dict[str, str] = { + "AddEntitiesRequestNamesItem": ".entities", + "AddEntitiesRequestNamesItemPersonMatchMode": ".entities", + "AddEntitiesRequestNamesItemType": ".entities", "AddEntitiesRequestPersonMatchMode": ".entities", "CreateMonitorsRequestNotifyFrequency": ".types", "UpdateMonitorsRequestNotifyFrequency": ".types", @@ -41,6 +49,9 @@ def __dir__(): __all__ = [ + "AddEntitiesRequestNamesItem", + "AddEntitiesRequestNamesItemPersonMatchMode", + "AddEntitiesRequestNamesItemType", "AddEntitiesRequestPersonMatchMode", "CreateMonitorsRequestNotifyFrequency", "UpdateMonitorsRequestNotifyFrequency", diff --git a/src/arcmira/monitors/alerts/client.py b/src/arcmira/monitors/alerts/client.py index b51b4a9..e7a68da 100644 --- a/src/arcmira/monitors/alerts/client.py +++ b/src/arcmira/monitors/alerts/client.py @@ -27,7 +27,7 @@ def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AlertListResponse: """ - The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- @@ -79,7 +79,7 @@ async def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AlertListResponse: """ - The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- diff --git a/src/arcmira/monitors/alerts/raw_client.py b/src/arcmira/monitors/alerts/raw_client.py index 515fe50..572ede3 100644 --- a/src/arcmira/monitors/alerts/raw_client.py +++ b/src/arcmira/monitors/alerts/raw_client.py @@ -29,7 +29,7 @@ def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[AlertListResponse]: """ - The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- @@ -149,7 +149,7 @@ async def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[AlertListResponse]: """ - The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the monitor (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- diff --git a/src/arcmira/monitors/entities/__init__.py b/src/arcmira/monitors/entities/__init__.py index 7b457fc..e5c7a53 100644 --- a/src/arcmira/monitors/entities/__init__.py +++ b/src/arcmira/monitors/entities/__init__.py @@ -6,8 +6,18 @@ from importlib import import_module if typing.TYPE_CHECKING: - from .types import AddEntitiesRequestPersonMatchMode -_dynamic_imports: typing.Dict[str, str] = {"AddEntitiesRequestPersonMatchMode": ".types"} + from .types import ( + AddEntitiesRequestNamesItem, + AddEntitiesRequestNamesItemPersonMatchMode, + AddEntitiesRequestNamesItemType, + AddEntitiesRequestPersonMatchMode, + ) +_dynamic_imports: typing.Dict[str, str] = { + "AddEntitiesRequestNamesItem": ".types", + "AddEntitiesRequestNamesItemPersonMatchMode": ".types", + "AddEntitiesRequestNamesItemType": ".types", + "AddEntitiesRequestPersonMatchMode": ".types", +} def __getattr__(attr_name: str) -> typing.Any: @@ -31,4 +41,9 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["AddEntitiesRequestPersonMatchMode"] +__all__ = [ + "AddEntitiesRequestNamesItem", + "AddEntitiesRequestNamesItemPersonMatchMode", + "AddEntitiesRequestNamesItemType", + "AddEntitiesRequestPersonMatchMode", +] diff --git a/src/arcmira/monitors/entities/client.py b/src/arcmira/monitors/entities/client.py index bce4cef..38c74f8 100644 --- a/src/arcmira/monitors/entities/client.py +++ b/src/arcmira/monitors/entities/client.py @@ -6,6 +6,7 @@ from ...core.request_options import RequestOptions from ...types.monitor_add_entities_response import MonitorAddEntitiesResponse from .raw_client import AsyncRawEntitiesClient, RawEntitiesClient +from .types.add_entities_request_names_item import AddEntitiesRequestNamesItem from .types.add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode # this is used as the default value for optional parameters @@ -31,25 +32,29 @@ def add( self, id: str, *, - entity_ids: typing.Sequence[str], idempotency_key: typing.Optional[str] = None, + entity_ids: typing.Optional[typing.Sequence[str]] = OMIT, + names: typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] = OMIT, person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorAddEntitiesResponse: """ - Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + Follows each entity ({ entity_ids: ["ent_..."] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str Monitor id. - entity_ids : typing.Sequence[str] - Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + entity_ids : typing.Optional[typing.Sequence[str]] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity. + + names : typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] + Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once. + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). @@ -71,13 +76,13 @@ def add( client.monitors.entities.add( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_ids=["entity_ids"], ) """ _response = self._raw_client.add( id, - entity_ids=entity_ids, idempotency_key=idempotency_key, + entity_ids=entity_ids, + names=names, person_match_mode=person_match_mode, request_options=request_options, ) @@ -103,25 +108,29 @@ async def add( self, id: str, *, - entity_ids: typing.Sequence[str], idempotency_key: typing.Optional[str] = None, + entity_ids: typing.Optional[typing.Sequence[str]] = OMIT, + names: typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] = OMIT, person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> MonitorAddEntitiesResponse: """ - Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + Follows each entity ({ entity_ids: ["ent_..."] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str Monitor id. - entity_ids : typing.Sequence[str] - Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + entity_ids : typing.Optional[typing.Sequence[str]] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity. + + names : typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] + Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once. + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). @@ -148,7 +157,6 @@ async def main() -> None: await client.monitors.entities.add( id="id", idempotency_key="8b2f6c3e-4d1a-4e7b-9c05-2f6a1b7d3e90", - entity_ids=["entity_ids"], ) @@ -156,8 +164,9 @@ async def main() -> None: """ _response = await self._raw_client.add( id, - entity_ids=entity_ids, idempotency_key=idempotency_key, + entity_ids=entity_ids, + names=names, person_match_mode=person_match_mode, request_options=request_options, ) diff --git a/src/arcmira/monitors/entities/raw_client.py b/src/arcmira/monitors/entities/raw_client.py index 961924a..659653a 100644 --- a/src/arcmira/monitors/entities/raw_client.py +++ b/src/arcmira/monitors/entities/raw_client.py @@ -10,6 +10,7 @@ from ...core.parse_error import ParsingError from ...core.pydantic_utilities import parse_obj_as from ...core.request_options import RequestOptions +from ...core.serialization import convert_and_respect_annotation_metadata from ...errors.bad_request_error import BadRequestError from ...errors.conflict_error import ConflictError from ...errors.forbidden_error import ForbiddenError @@ -19,6 +20,7 @@ from ...errors.unauthorized_error import UnauthorizedError from ...types.error import Error from ...types.monitor_add_entities_response import MonitorAddEntitiesResponse +from .types.add_entities_request_names_item import AddEntitiesRequestNamesItem from .types.add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode from pydantic import ValidationError @@ -34,25 +36,29 @@ def add( self, id: str, *, - entity_ids: typing.Sequence[str], idempotency_key: typing.Optional[str] = None, + entity_ids: typing.Optional[typing.Sequence[str]] = OMIT, + names: typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] = OMIT, person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[MonitorAddEntitiesResponse]: """ - Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + Follows each entity ({ entity_ids: ["ent_..."] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str Monitor id. - entity_ids : typing.Sequence[str] - Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + entity_ids : typing.Optional[typing.Sequence[str]] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity. + + names : typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] + Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once. + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). @@ -69,6 +75,9 @@ def add( method="POST", json={ "entity_ids": entity_ids, + "names": convert_and_respect_annotation_metadata( + object_=names, annotation=typing.Sequence[AddEntitiesRequestNamesItem], direction="write" + ), "person_match_mode": person_match_mode, }, headers={ @@ -183,25 +192,29 @@ async def add( self, id: str, *, - entity_ids: typing.Sequence[str], idempotency_key: typing.Optional[str] = None, + entity_ids: typing.Optional[typing.Sequence[str]] = OMIT, + names: typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] = OMIT, person_match_mode: typing.Optional[AddEntitiesRequestPersonMatchMode] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[MonitorAddEntitiesResponse]: """ - Follows each entity ({ entity_ids: ["ent_..."] }) in the monitor: the account's existing tracker for the entity is reused, else a tracker is created for the canonical entity (a merged id follows its redirect), then the trackers are attached. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids; duplicates count once. Each id gets one result in request order. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. + Follows each entity ({ entity_ids: ["ent_..."] }) and each exact name ({ names: [{ name, type }] }) in the monitor: the monitor account's existing tracker for the entity or name (compared case-insensitively) is reused, else a tracker is created under the monitor's account (the team owner on a team monitor) for the canonical entity (a merged id follows its redirect) or the name as given, then the trackers are attached, all in one write. Use names for something not yet indexed; a channel is named by its YouTube channel id, and a channel name answers 400 id_required. Attached trackers use the monitor's delivery settings. Supply 1 to 90 ids and names together; duplicates count once. Each gets one result, ids first then names, in request order; a names result carries name and type in place of entity_id. An id that cannot be followed comes back with attached: false and a reason (entity_not_found, entity_type_not_trackable, tracker_limit_reached, tracked_in_another_monitor) while the rest still attach; a tracker already in another monitor is left there and named in current_monitor_id. Requires the monitors:write and trackers:write scopes. Parameters ---------- id : str Monitor id. - entity_ids : typing.Sequence[str] - Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. 1 to 90; duplicates count once. A merged id follows its redirect to the canonical entity. - idempotency_key : typing.Optional[str] One key per intent, 1 to 255 printable ASCII characters (0x21 to 0x7E); anything else is 400 invalid_idempotency_key. Keys are scoped to the account, credential and resource family. The same key and normalized method, path and body returns the committed response with Idempotency-Replayed: true. A changed intent within the same family returns 409 idempotency_conflict. Monitor and tracker families have independent namespaces. Secret recovery is limited as described by the operation. + entity_ids : typing.Optional[typing.Sequence[str]] + Entity ids ("ent_...") to follow in this monitor, from GET /v1/entities/resolve or search. Duplicates count once. A merged id follows its redirect to the canonical entity. + + names : typing.Optional[typing.Sequence[AddEntitiesRequestNamesItem]] + Exact names to follow in this monitor, for a name not yet indexed or one you have no id for. The tracker is created under the monitor's account (the team owner on a team monitor) and attached in the same call; the account's tracker for the same name (compared case-insensitively) and type is reused. Duplicates count once. + person_match_mode : typing.Optional[AddEntitiesRequestPersonMatchMode] For person trackers this request creates: mentions (default) matches others talking about the person; appearances matches the person present as a speaker, host or guest; both accepts either. Other types ignore it, and a tracker that already exists keeps its own setting (change it with PATCH /v1/trackers/{id}). @@ -218,6 +231,9 @@ async def add( method="POST", json={ "entity_ids": entity_ids, + "names": convert_and_respect_annotation_metadata( + object_=names, annotation=typing.Sequence[AddEntitiesRequestNamesItem], direction="write" + ), "person_match_mode": person_match_mode, }, headers={ diff --git a/src/arcmira/monitors/entities/types/__init__.py b/src/arcmira/monitors/entities/types/__init__.py index 74501f3..cfd315e 100644 --- a/src/arcmira/monitors/entities/types/__init__.py +++ b/src/arcmira/monitors/entities/types/__init__.py @@ -6,9 +6,15 @@ from importlib import import_module if typing.TYPE_CHECKING: + from .add_entities_request_names_item import AddEntitiesRequestNamesItem + from .add_entities_request_names_item_person_match_mode import AddEntitiesRequestNamesItemPersonMatchMode + from .add_entities_request_names_item_type import AddEntitiesRequestNamesItemType from .add_entities_request_person_match_mode import AddEntitiesRequestPersonMatchMode _dynamic_imports: typing.Dict[str, str] = { - "AddEntitiesRequestPersonMatchMode": ".add_entities_request_person_match_mode" + "AddEntitiesRequestNamesItem": ".add_entities_request_names_item", + "AddEntitiesRequestNamesItemPersonMatchMode": ".add_entities_request_names_item_person_match_mode", + "AddEntitiesRequestNamesItemType": ".add_entities_request_names_item_type", + "AddEntitiesRequestPersonMatchMode": ".add_entities_request_person_match_mode", } @@ -33,4 +39,9 @@ def __dir__(): return sorted(lazy_attrs) -__all__ = ["AddEntitiesRequestPersonMatchMode"] +__all__ = [ + "AddEntitiesRequestNamesItem", + "AddEntitiesRequestNamesItemPersonMatchMode", + "AddEntitiesRequestNamesItemType", + "AddEntitiesRequestPersonMatchMode", +] diff --git a/src/arcmira/monitors/entities/types/add_entities_request_names_item.py b/src/arcmira/monitors/entities/types/add_entities_request_names_item.py new file mode 100644 index 0000000..f740aac --- /dev/null +++ b/src/arcmira/monitors/entities/types/add_entities_request_names_item.py @@ -0,0 +1,34 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ....core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .add_entities_request_names_item_person_match_mode import AddEntitiesRequestNamesItemPersonMatchMode +from .add_entities_request_names_item_type import AddEntitiesRequestNamesItemType + + +class AddEntitiesRequestNamesItem(UniversalBaseModel): + name: str = pydantic.Field() + """ + The exact name to watch, matched case-insensitively against analyzed media, so it can be followed before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters); a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel. + """ + + type: AddEntitiesRequestNamesItemType = pydantic.Field() + """ + person, organization (org is accepted), product, topic or channel. + """ + + person_match_mode: typing.Optional[AddEntitiesRequestNamesItemPersonMatchMode] = pydantic.Field(default=None) + """ + For a person tracker this request creates; defaults to the top-level person_match_mode. Other types ignore it. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/monitors/entities/types/add_entities_request_names_item_person_match_mode.py b/src/arcmira/monitors/entities/types/add_entities_request_names_item_person_match_mode.py new file mode 100644 index 0000000..f155385 --- /dev/null +++ b/src/arcmira/monitors/entities/types/add_entities_request_names_item_person_match_mode.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AddEntitiesRequestNamesItemPersonMatchMode = typing.Union[typing.Literal["mentions", "appearances", "both"], typing.Any] diff --git a/src/arcmira/monitors/entities/types/add_entities_request_names_item_type.py b/src/arcmira/monitors/entities/types/add_entities_request_names_item_type.py new file mode 100644 index 0000000..559fe58 --- /dev/null +++ b/src/arcmira/monitors/entities/types/add_entities_request_names_item_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +AddEntitiesRequestNamesItemType = typing.Union[ + typing.Literal["person", "organization", "org", "product", "topic", "channel"], typing.Any +] diff --git a/src/arcmira/trackers/alerts/client.py b/src/arcmira/trackers/alerts/client.py index a46859b..f085793 100644 --- a/src/arcmira/trackers/alerts/client.py +++ b/src/arcmira/trackers/alerts/client.py @@ -27,7 +27,7 @@ def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AlertListResponse: """ - The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- @@ -79,7 +79,7 @@ async def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AlertListResponse: """ - The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- diff --git a/src/arcmira/trackers/alerts/raw_client.py b/src/arcmira/trackers/alerts/raw_client.py index 5ea9aa4..f7852e1 100644 --- a/src/arcmira/trackers/alerts/raw_client.py +++ b/src/arcmira/trackers/alerts/raw_client.py @@ -29,7 +29,7 @@ def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> HttpResponse[AlertListResponse]: """ - The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- @@ -149,7 +149,7 @@ async def list( self, id: str, *, limit: typing.Optional[int] = None, request_options: typing.Optional[RequestOptions] = None ) -> AsyncHttpResponse[AlertListResponse]: """ - The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are public-ID forms that join directly against entity and mention rows; media_id and appearance_id are raw integer ids, matching the numeric ids used elsewhere in the API. Dispute a fired alert via POST /v1/feedback with type monitor_alert. + The newest limit alert deliveries for the tracker (default 25, at most 100), as a single page. has_more is true when older alerts exist past limit; this endpoint does not paginate, so next_cursor is always null and a larger limit reads further. entity_id ("ent_{n}") and mention_id ("men_{n}") are the ids GET /v1/entities/{id} and GET /v1/mentions use, and video_id is the YouTube video id that GET /v1/transcripts/{video_id} reads. Dispute a fired alert via POST /v1/feedback with type monitor_alert. Parameters ---------- diff --git a/src/arcmira/trackers/client.py b/src/arcmira/trackers/client.py index 1c06c88..a1dfae0 100644 --- a/src/arcmira/trackers/client.py +++ b/src/arcmira/trackers/client.py @@ -86,7 +86,7 @@ def create( Parameters ---------- entity_name : str - The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. @@ -378,7 +378,7 @@ async def create( Parameters ---------- entity_name : str - The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. diff --git a/src/arcmira/trackers/raw_client.py b/src/arcmira/trackers/raw_client.py index 6f7f2e7..147dd24 100644 --- a/src/arcmira/trackers/raw_client.py +++ b/src/arcmira/trackers/raw_client.py @@ -161,7 +161,7 @@ def create( Parameters ---------- entity_name : str - The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. @@ -771,7 +771,7 @@ async def create( Parameters ---------- entity_name : str - The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name + type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. + The exact name to watch, matched case-insensitively against analyzed media, so a tracker can exist before the entity is indexed. For a channel, the YouTube channel id (UC plus 22 characters), never a name: a channel name answers 400 id_required naming GET /v1/entities/resolve?q=...&type=channel and best.youtube_channel_id. Required on create. Creating a duplicate (same name, compared case-insensitively, and type) returns 409 tracker_already_exists with the existing tracker id in error.details.existing_id. entity_type : CreateTrackersRequestEntityType Entity type of the tracked entity. Required on create. org is accepted for organization, and the tracker answers organization. diff --git a/src/arcmira/trackers/types/create_trackers_request_entity_type.py b/src/arcmira/trackers/types/create_trackers_request_entity_type.py index d4f3b03..4120a43 100644 --- a/src/arcmira/trackers/types/create_trackers_request_entity_type.py +++ b/src/arcmira/trackers/types/create_trackers_request_entity_type.py @@ -3,5 +3,5 @@ import typing CreateTrackersRequestEntityType = typing.Union[ - typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any + typing.Literal["person", "organization", "org", "product", "topic", "channel"], typing.Any ] diff --git a/src/arcmira/transcripts/client.py b/src/arcmira/transcripts/client.py index 296c709..c7fdd25 100644 --- a/src/arcmira/transcripts/client.py +++ b/src/arcmira/transcripts/client.py @@ -128,11 +128,12 @@ def get( timestamps: typing.Optional[bool] = None, start: typing.Optional[float] = None, end: typing.Optional[float] = None, + retry: typing.Optional[bool] = None, refresh: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptResult: """ - Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. Parameters ---------- @@ -140,7 +141,7 @@ def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -154,6 +155,9 @@ def get( end : typing.Optional[float] Window end in seconds, greater than start and no greater than the video duration. Send start and end together. + retry : typing.Optional[bool] + Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing. + refresh : typing.Optional[bool] Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. @@ -163,7 +167,7 @@ def get( Returns ------- TranscriptResult - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last purchase for this video failed; job and last_attempt say why, nothing was bought, and retry=true buys again. Examples -------- @@ -183,6 +187,7 @@ def get( timestamps=timestamps, start=start, end=end, + retry=retry, refresh=refresh, request_options=request_options, ) @@ -391,11 +396,12 @@ async def get( timestamps: typing.Optional[bool] = None, start: typing.Optional[float] = None, end: typing.Optional[float] = None, + retry: typing.Optional[bool] = None, refresh: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> TranscriptResult: """ - Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. Parameters ---------- @@ -403,7 +409,7 @@ async def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -417,6 +423,9 @@ async def get( end : typing.Optional[float] Window end in seconds, greater than start and no greater than the video duration. Send start and end together. + retry : typing.Optional[bool] + Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing. + refresh : typing.Optional[bool] Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. @@ -426,7 +435,7 @@ async def get( Returns ------- TranscriptResult - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last purchase for this video failed; job and last_attempt say why, nothing was bought, and retry=true buys again. Examples -------- @@ -454,6 +463,7 @@ async def main() -> None: timestamps=timestamps, start=start, end=end, + retry=retry, refresh=refresh, request_options=request_options, ) diff --git a/src/arcmira/transcripts/raw_client.py b/src/arcmira/transcripts/raw_client.py index 824d251..55c23ea 100644 --- a/src/arcmira/transcripts/raw_client.py +++ b/src/arcmira/transcripts/raw_client.py @@ -230,11 +230,12 @@ def get( timestamps: typing.Optional[bool] = None, start: typing.Optional[float] = None, end: typing.Optional[float] = None, + retry: typing.Optional[bool] = None, refresh: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> HttpResponse[TranscriptResult]: """ - Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. Parameters ---------- @@ -242,7 +243,7 @@ def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -256,6 +257,9 @@ def get( end : typing.Optional[float] Window end in seconds, greater than start and no greater than the video duration. Send start and end together. + retry : typing.Optional[bool] + Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing. + refresh : typing.Optional[bool] Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. @@ -265,7 +269,7 @@ def get( Returns ------- HttpResponse[TranscriptResult] - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last purchase for this video failed; job and last_attempt say why, nothing was bought, and retry=true buys again. """ _response = self._client_wrapper.httpx_client.request( f"v1/transcripts/{encode_path_param(video_id)}", @@ -276,6 +280,7 @@ def get( "timestamps": timestamps, "start": start, "end": end, + "retry": retry, "refresh": refresh, }, request_options=request_options, @@ -831,11 +836,12 @@ async def get( timestamps: typing.Optional[bool] = None, start: typing.Optional[float] = None, end: typing.Optional[float] = None, + retry: typing.Optional[bool] = None, refresh: typing.Optional[bool] = None, request_options: typing.Optional[RequestOptions] = None, ) -> AsyncHttpResponse[TranscriptResult]: """ - Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium responses retain revision and line indexes for corrections. + Caption retrieval costs one row per started 15 minutes. quality=premium is one read: an owned transcript answers 200 ready at zero rows; otherwise this call buys the whole video within the account's plan and on-demand budget, included credits first and then on-demand money up to the account limit, and answers 202 pending with the job and Retry-After until the transcript is ready. Read again after Retry-After; repeated reads join the same purchase and never buy twice. When the last purchase for the video failed, the read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again. When the plan or the budget blocks, 403 paid_plan_required (with unlock) or 402 quota_exceeded or spend_limit_exceeded carries the price in quote and nothing is charged. A default-premium account with nothing owned reads captions with a note. start/end only trim the returned content; language selects caption tracks, timestamps=false returns paragraphs. Premium lines carry speaker and index, and the body carries speakers and revision. Parameters ---------- @@ -843,7 +849,7 @@ async def get( YouTube video id, 11 characters. quality : typing.Optional[GetTranscriptsRequestQuality] - captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. + captions reads creator or automatic captions at 1 row per started 15 minutes. premium is one read: an owned transcript returns 200 state ready at zero rows; otherwise the read buys the whole video within the account's plan and on-demand budget (included credits first, then on-demand money up to the account limit) and returns 202 state pending with the job until it is ready. When the last purchase for the video failed it answers 200 state failed and buys again only with retry=true. 402 quota_exceeded or spend_limit_exceeded and 403 paid_plan_required carry the price in quote. It never substitutes captions. Default captions unless changed in account settings. language : typing.Optional[str] Comma-separated caption language priority list, at most 5, tried in order (e.g. "de,en"). Use asr for the first automatic track and asr- for a specific one. Default en. languages[] in the response lists every track the video offers. @@ -857,6 +863,9 @@ async def get( end : typing.Optional[float] Window end in seconds, greater than start and no greater than the video duration. Send start and end together. + retry : typing.Optional[bool] + Premium only; captions with retry=true returns invalid_query. When the last Premium purchase for this video failed, a read answers 200 state failed with the job and last_attempt and buys nothing; retry=true buys it again under the same quote, budget and one-purchase rules as the first read. While that refund is still settling (job.status refund_pending) even retry=true answers state failed. Without a failed purchase it changes nothing. + refresh : typing.Optional[bool] Captions only; Premium with refresh=true returns invalid_query. Refetch the caption track from YouTube instead of serving the stored copy. Available only for videos outside our index; a pipeline-owned video refuses it with invalid_query. @@ -866,7 +875,7 @@ async def get( Returns ------- AsyncHttpResponse[TranscriptResult] - state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). + state ready: the transcript (video, quality, source, language, languages, lines or paragraphs, speakers and revision on Premium, rows_billed, as_of, note). state failed (Premium only): the last purchase for this video failed; job and last_attempt say why, nothing was bought, and retry=true buys again. """ _response = await self._client_wrapper.httpx_client.request( f"v1/transcripts/{encode_path_param(video_id)}", @@ -877,6 +886,7 @@ async def get( "timestamps": timestamps, "start": start, "end": end, + "retry": retry, "refresh": refresh, }, request_options=request_options, diff --git a/src/arcmira/types/__init__.py b/src/arcmira/types/__init__.py index 3f16552..8769dfa 100644 --- a/src/arcmira/types/__init__.py +++ b/src/arcmira/types/__init__.py @@ -22,14 +22,6 @@ from .channel_sponsors_response import ChannelSponsorsResponse from .channel_sponsors_response_access import ChannelSponsorsResponseAccess from .channel_sponsors_response_access_details import ChannelSponsorsResponseAccessDetails - from .channel_sponsors_response_access_details_quote import ChannelSponsorsResponseAccessDetailsQuote - from .channel_sponsors_response_access_details_quote_charge import ChannelSponsorsResponseAccessDetailsQuoteCharge - from .channel_sponsors_response_access_details_quote_charge_from import ( - ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, - ) - from .channel_sponsors_response_access_details_quote_charge_unit import ( - ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, - ) from .channel_sponsors_response_access_gate import ChannelSponsorsResponseAccessGate from .channel_sponsors_response_access_reason import ChannelSponsorsResponseAccessReason from .channel_sponsors_response_access_type import ChannelSponsorsResponseAccessType @@ -48,14 +40,6 @@ from .entity_momentum_response import EntityMomentumResponse from .entity_momentum_response_access import EntityMomentumResponseAccess from .entity_momentum_response_access_details import EntityMomentumResponseAccessDetails - from .entity_momentum_response_access_details_quote import EntityMomentumResponseAccessDetailsQuote - from .entity_momentum_response_access_details_quote_charge import EntityMomentumResponseAccessDetailsQuoteCharge - from .entity_momentum_response_access_details_quote_charge_from import ( - EntityMomentumResponseAccessDetailsQuoteChargeFrom, - ) - from .entity_momentum_response_access_details_quote_charge_unit import ( - EntityMomentumResponseAccessDetailsQuoteChargeUnit, - ) from .entity_momentum_response_access_gate import EntityMomentumResponseAccessGate from .entity_momentum_response_access_reason import EntityMomentumResponseAccessReason from .entity_momentum_response_access_type import EntityMomentumResponseAccessType @@ -73,10 +57,6 @@ from .error import Error from .error_error import ErrorError from .error_error_details import ErrorErrorDetails - from .error_error_details_quote import ErrorErrorDetailsQuote - from .error_error_details_quote_charge import ErrorErrorDetailsQuoteCharge - from .error_error_details_quote_charge_from import ErrorErrorDetailsQuoteChargeFrom - from .error_error_details_quote_charge_unit import ErrorErrorDetailsQuoteChargeUnit from .error_error_gate import ErrorErrorGate from .error_error_reason import ErrorErrorReason from .error_error_type import ErrorErrorType @@ -162,6 +142,7 @@ from .monitor_email_recipients_item_status import MonitorEmailRecipientsItemStatus from .monitor_entity_result import MonitorEntityResult from .monitor_entity_result_reason import MonitorEntityResultReason + from .monitor_entity_result_type import MonitorEntityResultType from .monitor_list_response import MonitorListResponse from .monitor_list_response_monitors_item import MonitorListResponseMonitorsItem from .monitor_list_response_monitors_item_slack_integration import MonitorListResponseMonitorsItemSlackIntegration @@ -183,6 +164,10 @@ from .recommendation_list_response import RecommendationListResponse from .recommendation_media import RecommendationMedia from .recommendation_media_source_channel import RecommendationMediaSourceChannel + from .refused_quote import RefusedQuote + from .refused_quote_charge import RefusedQuoteCharge + from .refused_quote_charge_from import RefusedQuoteChargeFrom + from .refused_quote_charge_unit import RefusedQuoteChargeUnit from .resolve_candidate import ResolveCandidate from .resolve_candidate_match import ResolveCandidateMatch from .resolve_suggestion import ResolveSuggestion @@ -201,6 +186,10 @@ from .tracker import Tracker from .tracker_list_response import TrackerListResponse from .tracker_mutation_response import TrackerMutationResponse + from .transcript_failed import TranscriptFailed + from .transcript_failed_last_attempt import TranscriptFailedLastAttempt + from .transcript_failed_last_attempt_status import TranscriptFailedLastAttemptStatus + from .transcript_failed_quality import TranscriptFailedQuality from .transcript_job import TranscriptJob from .transcript_job_charge import TranscriptJobCharge from .transcript_job_charge_from import TranscriptJobChargeFrom @@ -222,10 +211,6 @@ from .transcript_response import TranscriptResponse from .transcript_response_access import TranscriptResponseAccess from .transcript_response_access_details import TranscriptResponseAccessDetails - from .transcript_response_access_details_quote import TranscriptResponseAccessDetailsQuote - from .transcript_response_access_details_quote_charge import TranscriptResponseAccessDetailsQuoteCharge - from .transcript_response_access_details_quote_charge_from import TranscriptResponseAccessDetailsQuoteChargeFrom - from .transcript_response_access_details_quote_charge_unit import TranscriptResponseAccessDetailsQuoteChargeUnit from .transcript_response_access_gate import TranscriptResponseAccessGate from .transcript_response_access_reason import TranscriptResponseAccessReason from .transcript_response_access_type import TranscriptResponseAccessType @@ -237,19 +222,16 @@ from .transcript_response_range import TranscriptResponseRange from .transcript_response_source import TranscriptResponseSource from .transcript_response_speakers_item import TranscriptResponseSpeakersItem - from .transcript_result import TranscriptResult, TranscriptResult_Pending, TranscriptResult_Ready + from .transcript_result import ( + TranscriptResult, + TranscriptResult_Failed, + TranscriptResult_Pending, + TranscriptResult_Ready, + ) from .transcript_search_chunk import TranscriptSearchChunk from .transcript_search_response import TranscriptSearchResponse from .transcript_search_response_access import TranscriptSearchResponseAccess from .transcript_search_response_access_details import TranscriptSearchResponseAccessDetails - from .transcript_search_response_access_details_quote import TranscriptSearchResponseAccessDetailsQuote - from .transcript_search_response_access_details_quote_charge import TranscriptSearchResponseAccessDetailsQuoteCharge - from .transcript_search_response_access_details_quote_charge_from import ( - TranscriptSearchResponseAccessDetailsQuoteChargeFrom, - ) - from .transcript_search_response_access_details_quote_charge_unit import ( - TranscriptSearchResponseAccessDetailsQuoteChargeUnit, - ) from .transcript_search_response_access_gate import TranscriptSearchResponseAccessGate from .transcript_search_response_access_reason import TranscriptSearchResponseAccessReason from .transcript_search_response_access_type import TranscriptSearchResponseAccessType @@ -286,10 +268,6 @@ "ChannelSponsorsResponse": ".channel_sponsors_response", "ChannelSponsorsResponseAccess": ".channel_sponsors_response_access", "ChannelSponsorsResponseAccessDetails": ".channel_sponsors_response_access_details", - "ChannelSponsorsResponseAccessDetailsQuote": ".channel_sponsors_response_access_details_quote", - "ChannelSponsorsResponseAccessDetailsQuoteCharge": ".channel_sponsors_response_access_details_quote_charge", - "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom": ".channel_sponsors_response_access_details_quote_charge_from", - "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit": ".channel_sponsors_response_access_details_quote_charge_unit", "ChannelSponsorsResponseAccessGate": ".channel_sponsors_response_access_gate", "ChannelSponsorsResponseAccessReason": ".channel_sponsors_response_access_reason", "ChannelSponsorsResponseAccessType": ".channel_sponsors_response_access_type", @@ -308,10 +286,6 @@ "EntityMomentumResponse": ".entity_momentum_response", "EntityMomentumResponseAccess": ".entity_momentum_response_access", "EntityMomentumResponseAccessDetails": ".entity_momentum_response_access_details", - "EntityMomentumResponseAccessDetailsQuote": ".entity_momentum_response_access_details_quote", - "EntityMomentumResponseAccessDetailsQuoteCharge": ".entity_momentum_response_access_details_quote_charge", - "EntityMomentumResponseAccessDetailsQuoteChargeFrom": ".entity_momentum_response_access_details_quote_charge_from", - "EntityMomentumResponseAccessDetailsQuoteChargeUnit": ".entity_momentum_response_access_details_quote_charge_unit", "EntityMomentumResponseAccessGate": ".entity_momentum_response_access_gate", "EntityMomentumResponseAccessReason": ".entity_momentum_response_access_reason", "EntityMomentumResponseAccessType": ".entity_momentum_response_access_type", @@ -329,10 +303,6 @@ "Error": ".error", "ErrorError": ".error_error", "ErrorErrorDetails": ".error_error_details", - "ErrorErrorDetailsQuote": ".error_error_details_quote", - "ErrorErrorDetailsQuoteCharge": ".error_error_details_quote_charge", - "ErrorErrorDetailsQuoteChargeFrom": ".error_error_details_quote_charge_from", - "ErrorErrorDetailsQuoteChargeUnit": ".error_error_details_quote_charge_unit", "ErrorErrorGate": ".error_error_gate", "ErrorErrorReason": ".error_error_reason", "ErrorErrorType": ".error_error_type", @@ -416,6 +386,7 @@ "MonitorEmailRecipientsItemStatus": ".monitor_email_recipients_item_status", "MonitorEntityResult": ".monitor_entity_result", "MonitorEntityResultReason": ".monitor_entity_result_reason", + "MonitorEntityResultType": ".monitor_entity_result_type", "MonitorListResponse": ".monitor_list_response", "MonitorListResponseMonitorsItem": ".monitor_list_response_monitors_item", "MonitorListResponseMonitorsItemSlackIntegration": ".monitor_list_response_monitors_item_slack_integration", @@ -437,6 +408,10 @@ "RecommendationListResponse": ".recommendation_list_response", "RecommendationMedia": ".recommendation_media", "RecommendationMediaSourceChannel": ".recommendation_media_source_channel", + "RefusedQuote": ".refused_quote", + "RefusedQuoteCharge": ".refused_quote_charge", + "RefusedQuoteChargeFrom": ".refused_quote_charge_from", + "RefusedQuoteChargeUnit": ".refused_quote_charge_unit", "ResolveCandidate": ".resolve_candidate", "ResolveCandidateMatch": ".resolve_candidate_match", "ResolveSuggestion": ".resolve_suggestion", @@ -453,6 +428,10 @@ "Tracker": ".tracker", "TrackerListResponse": ".tracker_list_response", "TrackerMutationResponse": ".tracker_mutation_response", + "TranscriptFailed": ".transcript_failed", + "TranscriptFailedLastAttempt": ".transcript_failed_last_attempt", + "TranscriptFailedLastAttemptStatus": ".transcript_failed_last_attempt_status", + "TranscriptFailedQuality": ".transcript_failed_quality", "TranscriptJob": ".transcript_job", "TranscriptJobCharge": ".transcript_job_charge", "TranscriptJobChargeFrom": ".transcript_job_charge_from", @@ -474,10 +453,6 @@ "TranscriptResponse": ".transcript_response", "TranscriptResponseAccess": ".transcript_response_access", "TranscriptResponseAccessDetails": ".transcript_response_access_details", - "TranscriptResponseAccessDetailsQuote": ".transcript_response_access_details_quote", - "TranscriptResponseAccessDetailsQuoteCharge": ".transcript_response_access_details_quote_charge", - "TranscriptResponseAccessDetailsQuoteChargeFrom": ".transcript_response_access_details_quote_charge_from", - "TranscriptResponseAccessDetailsQuoteChargeUnit": ".transcript_response_access_details_quote_charge_unit", "TranscriptResponseAccessGate": ".transcript_response_access_gate", "TranscriptResponseAccessReason": ".transcript_response_access_reason", "TranscriptResponseAccessType": ".transcript_response_access_type", @@ -490,16 +465,13 @@ "TranscriptResponseSource": ".transcript_response_source", "TranscriptResponseSpeakersItem": ".transcript_response_speakers_item", "TranscriptResult": ".transcript_result", + "TranscriptResult_Failed": ".transcript_result", "TranscriptResult_Pending": ".transcript_result", "TranscriptResult_Ready": ".transcript_result", "TranscriptSearchChunk": ".transcript_search_chunk", "TranscriptSearchResponse": ".transcript_search_response", "TranscriptSearchResponseAccess": ".transcript_search_response_access", "TranscriptSearchResponseAccessDetails": ".transcript_search_response_access_details", - "TranscriptSearchResponseAccessDetailsQuote": ".transcript_search_response_access_details_quote", - "TranscriptSearchResponseAccessDetailsQuoteCharge": ".transcript_search_response_access_details_quote_charge", - "TranscriptSearchResponseAccessDetailsQuoteChargeFrom": ".transcript_search_response_access_details_quote_charge_from", - "TranscriptSearchResponseAccessDetailsQuoteChargeUnit": ".transcript_search_response_access_details_quote_charge_unit", "TranscriptSearchResponseAccessGate": ".transcript_search_response_access_gate", "TranscriptSearchResponseAccessReason": ".transcript_search_response_access_reason", "TranscriptSearchResponseAccessType": ".transcript_search_response_access_type", @@ -560,10 +532,6 @@ def __dir__(): "ChannelSponsorsResponse", "ChannelSponsorsResponseAccess", "ChannelSponsorsResponseAccessDetails", - "ChannelSponsorsResponseAccessDetailsQuote", - "ChannelSponsorsResponseAccessDetailsQuoteCharge", - "ChannelSponsorsResponseAccessDetailsQuoteChargeFrom", - "ChannelSponsorsResponseAccessDetailsQuoteChargeUnit", "ChannelSponsorsResponseAccessGate", "ChannelSponsorsResponseAccessReason", "ChannelSponsorsResponseAccessType", @@ -582,10 +550,6 @@ def __dir__(): "EntityMomentumResponse", "EntityMomentumResponseAccess", "EntityMomentumResponseAccessDetails", - "EntityMomentumResponseAccessDetailsQuote", - "EntityMomentumResponseAccessDetailsQuoteCharge", - "EntityMomentumResponseAccessDetailsQuoteChargeFrom", - "EntityMomentumResponseAccessDetailsQuoteChargeUnit", "EntityMomentumResponseAccessGate", "EntityMomentumResponseAccessReason", "EntityMomentumResponseAccessType", @@ -603,10 +567,6 @@ def __dir__(): "Error", "ErrorError", "ErrorErrorDetails", - "ErrorErrorDetailsQuote", - "ErrorErrorDetailsQuoteCharge", - "ErrorErrorDetailsQuoteChargeFrom", - "ErrorErrorDetailsQuoteChargeUnit", "ErrorErrorGate", "ErrorErrorReason", "ErrorErrorType", @@ -690,6 +650,7 @@ def __dir__(): "MonitorEmailRecipientsItemStatus", "MonitorEntityResult", "MonitorEntityResultReason", + "MonitorEntityResultType", "MonitorListResponse", "MonitorListResponseMonitorsItem", "MonitorListResponseMonitorsItemSlackIntegration", @@ -711,6 +672,10 @@ def __dir__(): "RecommendationListResponse", "RecommendationMedia", "RecommendationMediaSourceChannel", + "RefusedQuote", + "RefusedQuoteCharge", + "RefusedQuoteChargeFrom", + "RefusedQuoteChargeUnit", "ResolveCandidate", "ResolveCandidateMatch", "ResolveSuggestion", @@ -727,6 +692,10 @@ def __dir__(): "Tracker", "TrackerListResponse", "TrackerMutationResponse", + "TranscriptFailed", + "TranscriptFailedLastAttempt", + "TranscriptFailedLastAttemptStatus", + "TranscriptFailedQuality", "TranscriptJob", "TranscriptJobCharge", "TranscriptJobChargeFrom", @@ -748,10 +717,6 @@ def __dir__(): "TranscriptResponse", "TranscriptResponseAccess", "TranscriptResponseAccessDetails", - "TranscriptResponseAccessDetailsQuote", - "TranscriptResponseAccessDetailsQuoteCharge", - "TranscriptResponseAccessDetailsQuoteChargeFrom", - "TranscriptResponseAccessDetailsQuoteChargeUnit", "TranscriptResponseAccessGate", "TranscriptResponseAccessReason", "TranscriptResponseAccessType", @@ -764,16 +729,13 @@ def __dir__(): "TranscriptResponseSource", "TranscriptResponseSpeakersItem", "TranscriptResult", + "TranscriptResult_Failed", "TranscriptResult_Pending", "TranscriptResult_Ready", "TranscriptSearchChunk", "TranscriptSearchResponse", "TranscriptSearchResponseAccess", "TranscriptSearchResponseAccessDetails", - "TranscriptSearchResponseAccessDetailsQuote", - "TranscriptSearchResponseAccessDetailsQuoteCharge", - "TranscriptSearchResponseAccessDetailsQuoteChargeFrom", - "TranscriptSearchResponseAccessDetailsQuoteChargeUnit", "TranscriptSearchResponseAccessGate", "TranscriptSearchResponseAccessReason", "TranscriptSearchResponseAccessType", diff --git a/src/arcmira/types/channel_sponsors_response_access.py b/src/arcmira/types/channel_sponsors_response_access.py index 4f2ca07..4d94466 100644 --- a/src/arcmira/types/channel_sponsors_response_access.py +++ b/src/arcmira/types/channel_sponsors_response_access.py @@ -58,16 +58,6 @@ class ChannelSponsorsResponseAccess(UniversalBaseModel): Present on rate gates. Mirrors the Retry-After header. """ - current_revision: typing.Optional[str] = pydantic.Field(default=None) - """ - On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction. - """ - - expected_seq: typing.Optional[int] = pydantic.Field(default=None) - """ - On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. - """ - details: typing.Optional[ChannelSponsorsResponseAccessDetails] = pydantic.Field(default=None) """ Machine data the refusal carries for you to act on. Present only on the codes that name a field here. diff --git a/src/arcmira/types/channel_sponsors_response_access_details.py b/src/arcmira/types/channel_sponsors_response_access_details.py index bf943ad..a1c1166 100644 --- a/src/arcmira/types/channel_sponsors_response_access_details.py +++ b/src/arcmira/types/channel_sponsors_response_access_details.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .channel_sponsors_response_access_details_quote import ChannelSponsorsResponseAccessDetailsQuote +from .refused_quote import RefusedQuote class ChannelSponsorsResponseAccessDetails(UniversalBaseModel): @@ -12,16 +12,7 @@ class ChannelSponsorsResponseAccessDetails(UniversalBaseModel): Machine data the refusal carries for you to act on. Present only on the codes that name a field here. """ - quote: typing.Optional[ChannelSponsorsResponseAccessDetailsQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - + quote: typing.Optional[RefusedQuote] = None existing_id: typing.Optional[str] = pydantic.Field(default=None) """ On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote.py b/src/arcmira/types/channel_sponsors_response_access_details_quote.py deleted file mode 100644 index 269911f..0000000 --- a/src/arcmira/types/channel_sponsors_response_access_details_quote.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .channel_sponsors_response_access_details_quote_charge import ChannelSponsorsResponseAccessDetailsQuoteCharge -from .transcript_quote import TranscriptQuote - - -class ChannelSponsorsResponseAccessDetailsQuote(TranscriptQuote): - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - charge: typing.Optional[ChannelSponsorsResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) - """ - The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py deleted file mode 100644 index 0a2c777..0000000 --- a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .channel_sponsors_response_access_details_quote_charge_from import ( - ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, -) -from .channel_sponsors_response_access_details_quote_charge_unit import ( - ChannelSponsorsResponseAccessDetailsQuoteChargeUnit, -) - - -class ChannelSponsorsResponseAccessDetailsQuoteCharge(UniversalBaseModel): - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - unit: ChannelSponsorsResponseAccessDetailsQuoteChargeUnit - amount: float - from_: typing_extensions.Annotated[ - ChannelSponsorsResponseAccessDetailsQuoteChargeFrom, - FieldMetadata(alias="from"), - pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), - ] - """ - Where the charge would come from at the current balance. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py deleted file mode 100644 index 13178a4..0000000 --- a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_from.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelSponsorsResponseAccessDetailsQuoteChargeFrom = typing.Union[ - typing.Literal["included", "on_demand", "mixed"], typing.Any -] diff --git a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py b/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py deleted file mode 100644 index 5d6ff6f..0000000 --- a/src/arcmira/types/channel_sponsors_response_access_details_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChannelSponsorsResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/entity_momentum_response_access.py b/src/arcmira/types/entity_momentum_response_access.py index 11b0237..881a8c3 100644 --- a/src/arcmira/types/entity_momentum_response_access.py +++ b/src/arcmira/types/entity_momentum_response_access.py @@ -58,16 +58,6 @@ class EntityMomentumResponseAccess(UniversalBaseModel): Present on rate gates. Mirrors the Retry-After header. """ - current_revision: typing.Optional[str] = pydantic.Field(default=None) - """ - On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction. - """ - - expected_seq: typing.Optional[int] = pydantic.Field(default=None) - """ - On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. - """ - details: typing.Optional[EntityMomentumResponseAccessDetails] = pydantic.Field(default=None) """ Machine data the refusal carries for you to act on. Present only on the codes that name a field here. diff --git a/src/arcmira/types/entity_momentum_response_access_details.py b/src/arcmira/types/entity_momentum_response_access_details.py index e9e4eed..ab55f58 100644 --- a/src/arcmira/types/entity_momentum_response_access_details.py +++ b/src/arcmira/types/entity_momentum_response_access_details.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .entity_momentum_response_access_details_quote import EntityMomentumResponseAccessDetailsQuote +from .refused_quote import RefusedQuote class EntityMomentumResponseAccessDetails(UniversalBaseModel): @@ -12,16 +12,7 @@ class EntityMomentumResponseAccessDetails(UniversalBaseModel): Machine data the refusal carries for you to act on. Present only on the codes that name a field here. """ - quote: typing.Optional[EntityMomentumResponseAccessDetailsQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - + quote: typing.Optional[RefusedQuote] = None existing_id: typing.Optional[str] = pydantic.Field(default=None) """ On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote.py b/src/arcmira/types/entity_momentum_response_access_details_quote.py deleted file mode 100644 index 5c08c64..0000000 --- a/src/arcmira/types/entity_momentum_response_access_details_quote.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .entity_momentum_response_access_details_quote_charge import EntityMomentumResponseAccessDetailsQuoteCharge -from .transcript_quote import TranscriptQuote - - -class EntityMomentumResponseAccessDetailsQuote(TranscriptQuote): - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - charge: typing.Optional[EntityMomentumResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) - """ - The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py deleted file mode 100644 index e416a78..0000000 --- a/src/arcmira/types/entity_momentum_response_access_details_quote_charge.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .entity_momentum_response_access_details_quote_charge_from import ( - EntityMomentumResponseAccessDetailsQuoteChargeFrom, -) -from .entity_momentum_response_access_details_quote_charge_unit import ( - EntityMomentumResponseAccessDetailsQuoteChargeUnit, -) - - -class EntityMomentumResponseAccessDetailsQuoteCharge(UniversalBaseModel): - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - unit: EntityMomentumResponseAccessDetailsQuoteChargeUnit - amount: float - from_: typing_extensions.Annotated[ - EntityMomentumResponseAccessDetailsQuoteChargeFrom, - FieldMetadata(alias="from"), - pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), - ] - """ - Where the charge would come from at the current balance. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py deleted file mode 100644 index 6543f79..0000000 --- a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_from.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityMomentumResponseAccessDetailsQuoteChargeFrom = typing.Union[ - typing.Literal["included", "on_demand", "mixed"], typing.Any -] diff --git a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py b/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py deleted file mode 100644 index 709bb2e..0000000 --- a/src/arcmira/types/entity_momentum_response_access_details_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -EntityMomentumResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/error_error.py b/src/arcmira/types/error_error.py index d9d11c9..6e2bab7 100644 --- a/src/arcmira/types/error_error.py +++ b/src/arcmira/types/error_error.py @@ -54,16 +54,6 @@ class ErrorError(UniversalBaseModel): Present on rate gates. Mirrors the Retry-After header. """ - current_revision: typing.Optional[str] = pydantic.Field(default=None) - """ - On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction. - """ - - expected_seq: typing.Optional[int] = pydantic.Field(default=None) - """ - On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. - """ - details: typing.Optional[ErrorErrorDetails] = pydantic.Field(default=None) """ Machine data the refusal carries for you to act on. Present only on the codes that name a field here. diff --git a/src/arcmira/types/error_error_details.py b/src/arcmira/types/error_error_details.py index 5b16cf8..f1b7e72 100644 --- a/src/arcmira/types/error_error_details.py +++ b/src/arcmira/types/error_error_details.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .error_error_details_quote import ErrorErrorDetailsQuote +from .refused_quote import RefusedQuote class ErrorErrorDetails(UniversalBaseModel): @@ -12,16 +12,7 @@ class ErrorErrorDetails(UniversalBaseModel): Machine data the refusal carries for you to act on. Present only on the codes that name a field here. """ - quote: typing.Optional[ErrorErrorDetailsQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - + quote: typing.Optional[RefusedQuote] = None existing_id: typing.Optional[str] = pydantic.Field(default=None) """ On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. diff --git a/src/arcmira/types/error_error_details_quote_charge_from.py b/src/arcmira/types/error_error_details_quote_charge_from.py deleted file mode 100644 index d5f7f9d..0000000 --- a/src/arcmira/types/error_error_details_quote_charge_from.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ErrorErrorDetailsQuoteChargeFrom = typing.Union[typing.Literal["included", "on_demand", "mixed"], typing.Any] diff --git a/src/arcmira/types/error_error_details_quote_charge_unit.py b/src/arcmira/types/error_error_details_quote_charge_unit.py deleted file mode 100644 index 66de813..0000000 --- a/src/arcmira/types/error_error_details_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ErrorErrorDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/monitor_add_entities_response.py b/src/arcmira/types/monitor_add_entities_response.py index e33ce22..4d2234f 100644 --- a/src/arcmira/types/monitor_add_entities_response.py +++ b/src/arcmira/types/monitor_add_entities_response.py @@ -15,7 +15,7 @@ class MonitorAddEntitiesResponse(UniversalBaseModel): results: typing.List[MonitorEntityResult] = pydantic.Field() """ - One result per distinct requested entity id, in request order. + One result per distinct requested entity id, then one per distinct requested name, each in request order. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/monitor_entity_result.py b/src/arcmira/types/monitor_entity_result.py index 08486f3..0dc29cd 100644 --- a/src/arcmira/types/monitor_entity_result.py +++ b/src/arcmira/types/monitor_entity_result.py @@ -5,12 +5,23 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .monitor_entity_result_reason import MonitorEntityResultReason +from .monitor_entity_result_type import MonitorEntityResultType class MonitorEntityResult(UniversalBaseModel): - entity_id: str = pydantic.Field() + entity_id: typing.Optional[str] = pydantic.Field(default=None) """ - The entity id as requested. + The entity id as requested. Present on an entity_ids result. + """ + + name: typing.Optional[str] = pydantic.Field(default=None) + """ + The name as requested. Present on a names result. + """ + + type: typing.Optional[MonitorEntityResultType] = pydantic.Field(default=None) + """ + The type as requested, org read as organization. Present on a names result. """ canonical_entity_id: typing.Optional[str] = pydantic.Field(default=None) @@ -35,7 +46,7 @@ class MonitorEntityResult(UniversalBaseModel): reason: typing.Optional[MonitorEntityResultReason] = pydantic.Field(default=None) """ - Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants). + Why attached is false. Values: entity_not_found (no entity has this id), entity_type_not_trackable (only person, organization, product, topic and channel entities can be tracked), tracker_limit_reached (the plan allows no more trackers; upgrade the plan for more), tracked_in_another_monitor (the account already follows this entity in current_monitor_id; it was left there, so move it with POST /v1/monitors/{id}/trackers if the user wants). A names result can only carry tracker_limit_reached or tracked_in_another_monitor. """ current_monitor_id: typing.Optional[str] = pydantic.Field(default=None) diff --git a/src/arcmira/types/monitor_entity_result_type.py b/src/arcmira/types/monitor_entity_result_type.py new file mode 100644 index 0000000..949a0c2 --- /dev/null +++ b/src/arcmira/types/monitor_entity_result_type.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +MonitorEntityResultType = typing.Union[ + typing.Literal["person", "organization", "product", "topic", "channel"], typing.Any +] diff --git a/src/arcmira/types/error_error_details_quote.py b/src/arcmira/types/refused_quote.py similarity index 58% rename from src/arcmira/types/error_error_details_quote.py rename to src/arcmira/types/refused_quote.py index 4edd8c2..1e7b401 100644 --- a/src/arcmira/types/error_error_details_quote.py +++ b/src/arcmira/types/refused_quote.py @@ -4,23 +4,23 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .error_error_details_quote_charge import ErrorErrorDetailsQuoteCharge +from .refused_quote_charge import RefusedQuoteCharge from .transcript_quote import TranscriptQuote -class ErrorErrorDetailsQuote(TranscriptQuote): +class RefusedQuote(TranscriptQuote): """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. + The refused price, on a priced refusal: quota_exceeded, spend_limit_exceeded and paid_plan_required. """ - charge: typing.Optional[ErrorErrorDetailsQuoteCharge] = pydantic.Field(default=None) + charge: typing.Optional[RefusedQuoteCharge] = pydantic.Field(default=None) """ What the purchase would charge at the current balance. Absent when no current price could be read. """ max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) """ - The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. + The on-demand money, in whole cents, this purchase needs beyond included credits at the current balance. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/error_error_details_quote_charge.py b/src/arcmira/types/refused_quote_charge.py similarity index 75% rename from src/arcmira/types/error_error_details_quote_charge.py rename to src/arcmira/types/refused_quote_charge.py index ce45bc0..5f55051 100644 --- a/src/arcmira/types/error_error_details_quote_charge.py +++ b/src/arcmira/types/refused_quote_charge.py @@ -6,19 +6,19 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from ..core.serialization import FieldMetadata -from .error_error_details_quote_charge_from import ErrorErrorDetailsQuoteChargeFrom -from .error_error_details_quote_charge_unit import ErrorErrorDetailsQuoteChargeUnit +from .refused_quote_charge_from import RefusedQuoteChargeFrom +from .refused_quote_charge_unit import RefusedQuoteChargeUnit -class ErrorErrorDetailsQuoteCharge(UniversalBaseModel): +class RefusedQuoteCharge(UniversalBaseModel): """ What the purchase would charge at the current balance. Absent when no current price could be read. """ - unit: ErrorErrorDetailsQuoteChargeUnit + unit: RefusedQuoteChargeUnit amount: float from_: typing_extensions.Annotated[ - ErrorErrorDetailsQuoteChargeFrom, + RefusedQuoteChargeFrom, FieldMetadata(alias="from"), pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), ] diff --git a/src/arcmira/types/refused_quote_charge_from.py b/src/arcmira/types/refused_quote_charge_from.py new file mode 100644 index 0000000..b30c9c6 --- /dev/null +++ b/src/arcmira/types/refused_quote_charge_from.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RefusedQuoteChargeFrom = typing.Union[typing.Literal["included", "on_demand", "mixed"], typing.Any] diff --git a/src/arcmira/types/refused_quote_charge_unit.py b/src/arcmira/types/refused_quote_charge_unit.py new file mode 100644 index 0000000..78e37e1 --- /dev/null +++ b/src/arcmira/types/refused_quote_charge_unit.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +RefusedQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/transcript_failed.py b/src/arcmira/types/transcript_failed.py new file mode 100644 index 0000000..fc94468 --- /dev/null +++ b/src/arcmira/types/transcript_failed.py @@ -0,0 +1,33 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .transcript_failed_last_attempt import TranscriptFailedLastAttempt +from .transcript_failed_quality import TranscriptFailedQuality +from .transcript_job import TranscriptJob + + +class TranscriptFailed(UniversalBaseModel): + quality: TranscriptFailedQuality + video_id: str + job: TranscriptJob + last_attempt: TranscriptFailedLastAttempt = pydantic.Field() + """ + The failed purchase in brief: job.status and job.error. + """ + + note: str = pydantic.Field() + """ + What to do next: read again with retry=true to buy the video again, or wait while the refund settles. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_failed_last_attempt.py b/src/arcmira/types/transcript_failed_last_attempt.py new file mode 100644 index 0000000..032bc87 --- /dev/null +++ b/src/arcmira/types/transcript_failed_last_attempt.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +import pydantic +from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel +from .transcript_failed_last_attempt_status import TranscriptFailedLastAttemptStatus + + +class TranscriptFailedLastAttempt(UniversalBaseModel): + """ + The failed purchase in brief: job.status and job.error. + """ + + status: TranscriptFailedLastAttemptStatus = pydantic.Field() + """ + How the last purchase ended: failed, refunded (the charge was returned), or refund_pending (the refund is still settling). + """ + + error: str = pydantic.Field() + """ + Why it failed. + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_failed_last_attempt_status.py b/src/arcmira/types/transcript_failed_last_attempt_status.py new file mode 100644 index 0000000..91e6021 --- /dev/null +++ b/src/arcmira/types/transcript_failed_last_attempt_status.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptFailedLastAttemptStatus = typing.Union[typing.Literal["failed", "refund_pending", "refunded"], typing.Any] diff --git a/src/arcmira/types/transcript_failed_quality.py b/src/arcmira/types/transcript_failed_quality.py new file mode 100644 index 0000000..a96ce55 --- /dev/null +++ b/src/arcmira/types/transcript_failed_quality.py @@ -0,0 +1,5 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +TranscriptFailedQuality = typing.Union[typing.Literal["premium"], typing.Any] diff --git a/src/arcmira/types/transcript_job.py b/src/arcmira/types/transcript_job.py index 8ea6449..32ccd73 100644 --- a/src/arcmira/types/transcript_job.py +++ b/src/arcmira/types/transcript_job.py @@ -57,12 +57,12 @@ class TranscriptJob(UniversalBaseModel): error: typing.Optional[str] = pydantic.Field(default=None) """ - Failure reason. Only present when state is failed or refunded. + Failure reason. Only present when state is failed or refunded, or status is refund_pending. """ refunded: typing.Optional[bool] = pydantic.Field(default=None) """ - True when the charge was returned. Only present when state is failed or refunded. + True when the charge was returned. Only present when state is failed or refunded, or status is refund_pending (false until the refund lands). """ created_at: str = pydantic.Field() @@ -77,7 +77,7 @@ class TranscriptJob(UniversalBaseModel): status_url: str = pydantic.Field() """ - Absolute URL of GET /v1/transcriptions/{id} for this job. + Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it prepares, 200 ready once it is, and 200 failed if it failed. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/transcript_response.py b/src/arcmira/types/transcript_response.py index 3d0fbaf..c554e3b 100644 --- a/src/arcmira/types/transcript_response.py +++ b/src/arcmira/types/transcript_response.py @@ -55,7 +55,7 @@ class TranscriptResponse(UniversalBaseModel): revision: typing.Optional[str] = pydantic.Field(default=None) """ - Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. Echo it on every correction; a 409 means it changed underneath you, so read again. + Premium reads only. Opaque id of the transcript you were served, the approved corrections on it, and who speaks each line. It changes when any of those change. """ range: typing.Optional[TranscriptResponseRange] = pydantic.Field(default=None) diff --git a/src/arcmira/types/transcript_response_access.py b/src/arcmira/types/transcript_response_access.py index 53249c5..316e82c 100644 --- a/src/arcmira/types/transcript_response_access.py +++ b/src/arcmira/types/transcript_response_access.py @@ -58,16 +58,6 @@ class TranscriptResponseAccess(UniversalBaseModel): Present on rate gates. Mirrors the Retry-After header. """ - current_revision: typing.Optional[str] = pydantic.Field(default=None) - """ - On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction. - """ - - expected_seq: typing.Optional[int] = pydantic.Field(default=None) - """ - On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. - """ - details: typing.Optional[TranscriptResponseAccessDetails] = pydantic.Field(default=None) """ Machine data the refusal carries for you to act on. Present only on the codes that name a field here. diff --git a/src/arcmira/types/transcript_response_access_details.py b/src/arcmira/types/transcript_response_access_details.py index 024f1c4..52e7138 100644 --- a/src/arcmira/types/transcript_response_access_details.py +++ b/src/arcmira/types/transcript_response_access_details.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_response_access_details_quote import TranscriptResponseAccessDetailsQuote +from .refused_quote import RefusedQuote class TranscriptResponseAccessDetails(UniversalBaseModel): @@ -12,16 +12,7 @@ class TranscriptResponseAccessDetails(UniversalBaseModel): Machine data the refusal carries for you to act on. Present only on the codes that name a field here. """ - quote: typing.Optional[TranscriptResponseAccessDetailsQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - + quote: typing.Optional[RefusedQuote] = None existing_id: typing.Optional[str] = pydantic.Field(default=None) """ On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. diff --git a/src/arcmira/types/transcript_response_access_details_quote.py b/src/arcmira/types/transcript_response_access_details_quote.py deleted file mode 100644 index 39b9da5..0000000 --- a/src/arcmira/types/transcript_response_access_details_quote.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .transcript_quote import TranscriptQuote -from .transcript_response_access_details_quote_charge import TranscriptResponseAccessDetailsQuoteCharge - - -class TranscriptResponseAccessDetailsQuote(TranscriptQuote): - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - charge: typing.Optional[TranscriptResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) - """ - The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_response_access_details_quote_charge.py b/src/arcmira/types/transcript_response_access_details_quote_charge.py deleted file mode 100644 index bb01d49..0000000 --- a/src/arcmira/types/transcript_response_access_details_quote_charge.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .transcript_response_access_details_quote_charge_from import TranscriptResponseAccessDetailsQuoteChargeFrom -from .transcript_response_access_details_quote_charge_unit import TranscriptResponseAccessDetailsQuoteChargeUnit - - -class TranscriptResponseAccessDetailsQuoteCharge(UniversalBaseModel): - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - unit: TranscriptResponseAccessDetailsQuoteChargeUnit - amount: float - from_: typing_extensions.Annotated[ - TranscriptResponseAccessDetailsQuoteChargeFrom, - FieldMetadata(alias="from"), - pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), - ] - """ - Where the charge would come from at the current balance. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_response_access_details_quote_charge_from.py b/src/arcmira/types/transcript_response_access_details_quote_charge_from.py deleted file mode 100644 index 0575305..0000000 --- a/src/arcmira/types/transcript_response_access_details_quote_charge_from.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptResponseAccessDetailsQuoteChargeFrom = typing.Union[ - typing.Literal["included", "on_demand", "mixed"], typing.Any -] diff --git a/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py b/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py deleted file mode 100644 index b328221..0000000 --- a/src/arcmira/types/transcript_response_access_details_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/src/arcmira/types/transcript_response_lines_item.py b/src/arcmira/types/transcript_response_lines_item.py index 1462344..319f6c1 100644 --- a/src/arcmira/types/transcript_response_lines_item.py +++ b/src/arcmira/types/transcript_response_lines_item.py @@ -25,7 +25,7 @@ class TranscriptResponseLinesItem(UniversalBaseModel): index: typing.Optional[int] = pydantic.Field(default=None) """ - Line index, present on every Premium line. Echo it as anchor.segmentIndex when you correct the line. + Line index, present on every Premium line. Stable within one revision. """ if IS_PYDANTIC_V2: diff --git a/src/arcmira/types/transcript_result.py b/src/arcmira/types/transcript_result.py index 8dedaee..5e4c92c 100644 --- a/src/arcmira/types/transcript_result.py +++ b/src/arcmira/types/transcript_result.py @@ -8,6 +8,8 @@ import typing_extensions from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .caption_track import CaptionTrack +from .transcript_failed_last_attempt import TranscriptFailedLastAttempt +from .transcript_failed_quality import TranscriptFailedQuality from .transcript_job import TranscriptJob from .transcript_pending_quality import TranscriptPendingQuality from .transcript_response_access import TranscriptResponseAccess @@ -48,6 +50,24 @@ class Config: extra = pydantic.Extra.allow +class TranscriptResult_Failed(UniversalBaseModel): + state: typing.Literal["failed"] = "failed" + quality: TranscriptFailedQuality + video_id: str + job: TranscriptJob + last_attempt: TranscriptFailedLastAttempt + note: str + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow + + class TranscriptResult_Pending(UniversalBaseModel): state: typing.Literal["pending"] = "pending" quality: TranscriptPendingQuality @@ -65,5 +85,6 @@ class Config: TranscriptResult = typing_extensions.Annotated[ - typing.Union[TranscriptResult_Ready, TranscriptResult_Pending], pydantic.Field(discriminator="state") + typing.Union[TranscriptResult_Ready, TranscriptResult_Failed, TranscriptResult_Pending], + pydantic.Field(discriminator="state"), ] diff --git a/src/arcmira/types/transcript_search_response_access.py b/src/arcmira/types/transcript_search_response_access.py index 6bc0301..e69c8e3 100644 --- a/src/arcmira/types/transcript_search_response_access.py +++ b/src/arcmira/types/transcript_search_response_access.py @@ -58,16 +58,6 @@ class TranscriptSearchResponseAccess(UniversalBaseModel): Present on rate gates. Mirrors the Retry-After header. """ - current_revision: typing.Optional[str] = pydantic.Field(default=None) - """ - On revision_mismatch and anchor_mismatch, the transcript revision to re-read before re-anchoring the correction. - """ - - expected_seq: typing.Optional[int] = pydantic.Field(default=None) - """ - On sequence_mismatch (HTTP 412), the seq the server expects next for this video. Rebase local counters onto it and resend under the same key. - """ - details: typing.Optional[TranscriptSearchResponseAccessDetails] = pydantic.Field(default=None) """ Machine data the refusal carries for you to act on. Present only on the codes that name a field here. diff --git a/src/arcmira/types/transcript_search_response_access_details.py b/src/arcmira/types/transcript_search_response_access_details.py index 5eb8f1e..b44e22f 100644 --- a/src/arcmira/types/transcript_search_response_access_details.py +++ b/src/arcmira/types/transcript_search_response_access_details.py @@ -4,7 +4,7 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from .transcript_search_response_access_details_quote import TranscriptSearchResponseAccessDetailsQuote +from .refused_quote import RefusedQuote class TranscriptSearchResponseAccessDetails(UniversalBaseModel): @@ -12,16 +12,7 @@ class TranscriptSearchResponseAccessDetails(UniversalBaseModel): Machine data the refusal carries for you to act on. Present only on the codes that name a field here. """ - quote: typing.Optional[TranscriptSearchResponseAccessDetailsQuote] = pydantic.Field(default=None) - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - existing_request_id: typing.Optional[str] = pydantic.Field(default=None) - """ - On max_charge_exceeded: the accepted purchase for this video that holds a higher money ceiling. Poll it at /v1/transcriptions/{id} instead of starting another. - """ - + quote: typing.Optional[RefusedQuote] = None existing_id: typing.Optional[str] = pydantic.Field(default=None) """ On tracker_already_exists, the existing tracker id. Reuse it instead of creating another tracker. diff --git a/src/arcmira/types/transcript_search_response_access_details_quote.py b/src/arcmira/types/transcript_search_response_access_details_quote.py deleted file mode 100644 index 75ba75c..0000000 --- a/src/arcmira/types/transcript_search_response_access_details_quote.py +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2 -from .transcript_quote import TranscriptQuote -from .transcript_search_response_access_details_quote_charge import TranscriptSearchResponseAccessDetailsQuoteCharge - - -class TranscriptSearchResponseAccessDetailsQuote(TranscriptQuote): - """ - The refused price, on a priced refusal: quota_exceeded, max_rows_exceeded, max_charge_exceeded, spend_limit_exceeded, purchase_authority_changed and paid_plan_required. - """ - - charge: typing.Optional[TranscriptSearchResponseAccessDetailsQuoteCharge] = pydantic.Field(default=None) - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - max_on_demand_cents: typing.Optional[int] = pydantic.Field(default=None) - """ - The money ceiling the current quote needs, in whole cents. Send at least this as max_on_demand_cents with a new intent. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge.py deleted file mode 100644 index 8762982..0000000 --- a/src/arcmira/types/transcript_search_response_access_details_quote_charge.py +++ /dev/null @@ -1,40 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -import typing_extensions -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel -from ..core.serialization import FieldMetadata -from .transcript_search_response_access_details_quote_charge_from import ( - TranscriptSearchResponseAccessDetailsQuoteChargeFrom, -) -from .transcript_search_response_access_details_quote_charge_unit import ( - TranscriptSearchResponseAccessDetailsQuoteChargeUnit, -) - - -class TranscriptSearchResponseAccessDetailsQuoteCharge(UniversalBaseModel): - """ - What the purchase would charge at the current balance. Absent when no current price could be read. - """ - - unit: TranscriptSearchResponseAccessDetailsQuoteChargeUnit - amount: float - from_: typing_extensions.Annotated[ - TranscriptSearchResponseAccessDetailsQuoteChargeFrom, - FieldMetadata(alias="from"), - pydantic.Field(alias="from", description="Where the charge would come from at the current balance."), - ] - """ - Where the charge would come from at the current balance. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py deleted file mode 100644 index 807ec89..0000000 --- a/src/arcmira/types/transcript_search_response_access_details_quote_charge_from.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptSearchResponseAccessDetailsQuoteChargeFrom = typing.Union[ - typing.Literal["included", "on_demand", "mixed"], typing.Any -] diff --git a/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py b/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py deleted file mode 100644 index a8e8a31..0000000 --- a/src/arcmira/types/transcript_search_response_access_details_quote_charge_unit.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TranscriptSearchResponseAccessDetailsQuoteChargeUnit = typing.Union[typing.Literal["rows", "credits"], typing.Any] diff --git a/tests/test_generation.py b/tests/test_generation.py index 85d570b..82ebe7d 100644 --- a/tests/test_generation.py +++ b/tests/test_generation.py @@ -38,10 +38,10 @@ def test_prepare_leaves_the_input_document_unchanged(self): TOOLS['prepare'](DOCUMENT, NAMES) self.assertEqual(DOCUMENT, before) - def test_transcript_result_discriminates_ready_and_pending(self): + def test_transcript_result_discriminates_ready_pending_and_failed(self): union = self.prepared['components']['schemas']['TranscriptResult'] self.assertEqual(union['discriminator']['propertyName'], 'state') - self.assertEqual(set(union['discriminator']['mapping']), {'ready', 'pending'}) + self.assertEqual(set(union['discriminator']['mapping']), {'ready', 'pending', 'failed'}) responses = self.prepared['paths']['/v1/transcripts/{video_id}']['get']['responses'] for code in ('200', '202'): self.assertEqual(responses[code]['content']['application/json']['schema'], {'$ref': '#/components/schemas/TranscriptResult'}) From 5862db9905b0861f2e761a0519fe5076b99e7d73 Mon Sep 17 00:00:00 2001 From: Zeal Caiden Date: Fri, 2 Oct 2026 21:15:45 -0700 Subject: [PATCH 3/4] Take the final spec wording: a Premium read transcribes, not prepares --- fern/openapi.json | 4 ++-- src/arcmira/types/transcript_job.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fern/openapi.json b/fern/openapi.json index d3d0ba2..c9a3cfe 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -6206,7 +6206,7 @@ }, "status_url": { "type": "string", - "description": "Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it prepares, 200 ready once it is, and 200 failed if it failed." + "description": "Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it transcribes, 200 ready once it is done, and 200 failed if it failed." } }, "required": [ @@ -6218,7 +6218,7 @@ "created_at", "status_url" ], - "description": "Your open Premium purchase for this video, when captions were served while it prepares." + "description": "Your open Premium purchase for this video, when captions were served while it transcribes." }, "TranscriptFailed": { "type": "object", diff --git a/src/arcmira/types/transcript_job.py b/src/arcmira/types/transcript_job.py index 32ccd73..398209e 100644 --- a/src/arcmira/types/transcript_job.py +++ b/src/arcmira/types/transcript_job.py @@ -12,7 +12,7 @@ class TranscriptJob(UniversalBaseModel): """ - Your open Premium purchase for this video, when captions were served while it prepares. + Your open Premium purchase for this video, when captions were served while it transcribes. """ id: str = pydantic.Field() @@ -77,7 +77,7 @@ class TranscriptJob(UniversalBaseModel): status_url: str = pydantic.Field() """ - Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it prepares, 200 ready once it is, and 200 failed if it failed. + Absolute URL to read again for this job: GET /v1/transcripts/{video_id}?quality=premium, which answers 202 while it transcribes, 200 ready once it is done, and 200 failed if it failed. """ if IS_PYDANTIC_V2: From 50c3239431c5b3f175146ee24c0b6dcf6e2690f6 Mon Sep 17 00:00:00 2001 From: Zeal Caiden Date: Fri, 2 Oct 2026 21:56:32 -0700 Subject: [PATCH 4/4] Regenerate from master's v1 document: usage drops hits --- CHANGELOG.md | 1 + fern/openapi.json | 26 --------------- src/arcmira/__init__.py | 3 -- src/arcmira/types/__init__.py | 3 -- src/arcmira/types/me_response_usage.py | 6 ---- src/arcmira/types/me_response_usage_hits.py | 36 --------------------- 6 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 src/arcmira/types/me_response_usage_hits.py diff --git a/CHANGELOG.md b/CHANGELOG.md index a394538..460cc0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Breaking changes from 0.3. - `MentionCountsResponse` replaces `published_after` and `published_before` with `window`. `FeedbackCorrectionResult` drops `new_mention_class`, `previous_mention_class`, `recommendation` and `rows_affected`. - `feedback.submit` no longer requires `query`. `type` stays required. - `trackers.create` follows a channel by its YouTube channel id in `entity_name`. A channel name raises `id_required`. +- `me.usage.hits` is gone. Monitor alerts cost credits now and count in `usage.credits`. Removed methods and their replacements. diff --git a/fern/openapi.json b/fern/openapi.json index c9a3cfe..d6fded7 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -1249,32 +1249,6 @@ "on_demand" ], "description": "The month in credits (1 credit is $0.001; a row is 4 credits). Present only when the credits ledger decides access." - }, - "hits": { - "type": "object", - "properties": { - "used": { - "type": "integer", - "description": "Monitor alerts delivered this month." - }, - "allowance": { - "type": [ - "integer", - "null" - ], - "description": "Monitor alerts the plan delivers a month. Null when the plan has no limit." - }, - "resets_at": { - "type": "string", - "description": "YYYY-MM-DD, the first day of next month (UTC), when the count resets." - } - }, - "required": [ - "used", - "allowance", - "resets_at" - ], - "description": "Monitor alerts this month. Alerts cost no credits; once the allowance is used, monitors keep matching but deliver nothing until the reset. Present only when the credits ledger decides access." } }, "required": [ diff --git a/src/arcmira/__init__.py b/src/arcmira/__init__.py index c0fcf84..1a93e08 100644 --- a/src/arcmira/__init__.py +++ b/src/arcmira/__init__.py @@ -112,7 +112,6 @@ MeResponseUsageCredits, MeResponseUsageCreditsOnDemand, MeResponseUsageCreditsPlan, - MeResponseUsageHits, MeSettingsResponse, Mention, MentionCountsResponse, @@ -418,7 +417,6 @@ "MeResponseUsageCredits": ".types", "MeResponseUsageCreditsOnDemand": ".types", "MeResponseUsageCreditsPlan": ".types", - "MeResponseUsageHits": ".types", "MeSettingsResponse": ".types", "Mention": ".types", "MentionCountsResponse": ".types", @@ -729,7 +727,6 @@ def __dir__(): "MeResponseUsageCredits", "MeResponseUsageCreditsOnDemand", "MeResponseUsageCreditsPlan", - "MeResponseUsageHits", "MeSettingsResponse", "Mention", "MentionCountsResponse", diff --git a/src/arcmira/types/__init__.py b/src/arcmira/types/__init__.py index 8769dfa..86e85f4 100644 --- a/src/arcmira/types/__init__.py +++ b/src/arcmira/types/__init__.py @@ -113,7 +113,6 @@ from .me_response_usage_credits import MeResponseUsageCredits from .me_response_usage_credits_on_demand import MeResponseUsageCreditsOnDemand from .me_response_usage_credits_plan import MeResponseUsageCreditsPlan - from .me_response_usage_hits import MeResponseUsageHits from .me_settings_response import MeSettingsResponse from .mention import Mention from .mention_counts_response import MentionCountsResponse @@ -357,7 +356,6 @@ "MeResponseUsageCredits": ".me_response_usage_credits", "MeResponseUsageCreditsOnDemand": ".me_response_usage_credits_on_demand", "MeResponseUsageCreditsPlan": ".me_response_usage_credits_plan", - "MeResponseUsageHits": ".me_response_usage_hits", "MeSettingsResponse": ".me_settings_response", "Mention": ".mention", "MentionCountsResponse": ".mention_counts_response", @@ -621,7 +619,6 @@ def __dir__(): "MeResponseUsageCredits", "MeResponseUsageCreditsOnDemand", "MeResponseUsageCreditsPlan", - "MeResponseUsageHits", "MeSettingsResponse", "Mention", "MentionCountsResponse", diff --git a/src/arcmira/types/me_response_usage.py b/src/arcmira/types/me_response_usage.py index b949e79..5817d44 100644 --- a/src/arcmira/types/me_response_usage.py +++ b/src/arcmira/types/me_response_usage.py @@ -5,7 +5,6 @@ import pydantic from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel from .me_response_usage_credits import MeResponseUsageCredits -from .me_response_usage_hits import MeResponseUsageHits class MeResponseUsage(UniversalBaseModel): @@ -34,11 +33,6 @@ class MeResponseUsage(UniversalBaseModel): The month in credits (1 credit is $0.001; a row is 4 credits). Present only when the credits ledger decides access. """ - hits: typing.Optional[MeResponseUsageHits] = pydantic.Field(default=None) - """ - Monitor alerts this month. Alerts cost no credits; once the allowance is used, monitors keep matching but deliver nothing until the reset. Present only when the credits ledger decides access. - """ - if IS_PYDANTIC_V2: model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 else: diff --git a/src/arcmira/types/me_response_usage_hits.py b/src/arcmira/types/me_response_usage_hits.py deleted file mode 100644 index 1093c14..0000000 --- a/src/arcmira/types/me_response_usage_hits.py +++ /dev/null @@ -1,36 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -import pydantic -from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel - - -class MeResponseUsageHits(UniversalBaseModel): - """ - Monitor alerts this month. Alerts cost no credits; once the allowance is used, monitors keep matching but deliver nothing until the reset. Present only when the credits ledger decides access. - """ - - used: int = pydantic.Field() - """ - Monitor alerts delivered this month. - """ - - allowance: typing.Optional[int] = pydantic.Field(default=None) - """ - Monitor alerts the plan delivers a month. Null when the plan has no limit. - """ - - resets_at: str = pydantic.Field() - """ - YYYY-MM-DD, the first day of next month (UTC), when the count resets. - """ - - if IS_PYDANTIC_V2: - model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 - else: - - class Config: - frozen = True - smart_union = True - extra = pydantic.Extra.allow