diff --git a/.bumpversion.cfg b/.bumpversion.cfg deleted file mode 100644 index ae666b5..0000000 --- a/.bumpversion.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[bumpversion] -current_version = 2.2.3 -commit = False -tag = False - -[bumpversion:file:setup.py] diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e76c370 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,33 @@ +--- +applyTo: "**" +--- + +# Project Guidelines + +## General Coding Standards + +- Use descriptive variable and function names. +- Add concise comments to explain complex logic. +- Prioritize clear, maintainable code. + +## Python Style Requirements + +- Use Python for all new code. +- Prefer single-quoted strings for consistency. +- Use `f-strings` for string formatting. +- Use two spaces for indentation. +- Use concise and correct type annotations as much as possible. +- Prefer early-returns over if-else constructs. +- Follow functional programming principles where possible. +- Ensure code is compatible with Python 3.9+. + +## Logging Guidance + +- Use the `logging` module for logging. +- Create a logger instance for each module a la `logger = logging.getLogger(__name__)`. +- Make use of the `rich` library to enhance logging output when possible. + +## Project Conventions +- Use `uv run pytest -xvs &> output.txt` to run tests. + - There is a bug in the current version of Copilot that causes it to not read the output of commands directly. This workaround allows you to read the output from `output.txt` instead. + - The tests are fast and inexpensive to run, so please favour running *all* of them to avoid missing any issues. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..9d082af --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,37 @@ +name: pytest + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Set up Python ${{ matrix.python-version }} with uv + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install the project + run: uv sync --locked --dev + + - name: Run tests with pytest + run: uv run pytest + + - name: Run code formatting checks with ruff + run: uv run ruff check diff --git a/.gitignore b/.gitignore index 81d5e49..5dc9322 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,4 @@ ENV/ # Rope project settings .ropeproject test/.resources/ +output.txt diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 1ccd819..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include requirements.txt - diff --git a/README.md b/README.md index 8023da7..8177a4f 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ For information about the API itself, head on over to our ## Installation -Just use everyone's favourite python package installer: `pip` +Just use everyone's new favourite python package manager: `uv` ```bash -pip install python-dialpad +uv add python-dialpad ``` ## Usage @@ -29,7 +29,7 @@ from dialpad import DialpadClient dp_client = DialpadClient(sandbox=True, token='API_TOKEN_HERE') -print(dp_client.user.get(user_id='1234567')) +print(dp_client.users.get(user_id='1234567')) ``` ### Client Constructor Arguments @@ -42,14 +42,18 @@ print(dp_client.user.get(user_id='1234567')) ### API Resources -In general, each resource that we support in our public API will be exposed as properties of the -client object. For example, the `User` resource can be accessed using the `user` property (as +In general, resources that we support in our public API will be exposed as properties of the +client object. For example, the `User` resource can be accessed using the `users` property (as demonstrated above). Each of these resource properties will expose related HTTP methods as methods of that resource property. -For example, `GET /api/v2/users/{id}` translates to `dp_client.user.get('the_user_id')`. +For example, `GET /api/v2/users/{id}` translates to `dp_client.users.get('a_user_id')`. + +When in doubt, type annotations and docstrings are sourced directly from the Dialpad API spec, and +should behave well with most editors' autocomplete/tooltip features: +![user list method tooltip](./docs/images/tooltip_example.png) ### API Responses @@ -65,98 +69,39 @@ from dialpad import DialpadClient dp_client = DialpadClient(sandbox=True, token='API_TOKEN_HERE') -for user in dp_client.user.list(): +for user in dp_client.users.list(): print(user) ``` ## Development -### Testing - -That's right, the testing section is first in line! Before you start diving in, let's just make sure your environment is set up properly, and that the tests are running buttery-smooth. - -Assuming you've already cloned the repository, all you'll need to do is install `tox`, and run the command against the appropriate environment. - -* Install the `tox` package. - ```shell - $ pip install tox - ``` - -* Run the tests - ```shell - $ tox - ``` - Optionaly, you can specify an environment to run the tests against. For eg: - ```shell - $ tox -e py3 - ``` -That was easy :) - -Neato! - -### Adding New Resources - -Most of the changes to this library will probably just be adding support for additional resources -and endpoints that we expose in the API, so let's start with how to add a new resource. - -Each resource exposed by this library should have its own python file under the `dialpad/resources` -directory, and should define a single `class` that inherits from `DialpadResource`. - -The class itself should set the `_resource_path` class property to a list of strings such -that `'/api/v2/' + '/'.join(_resource_path)` corresponds to the API path for that resource. - -Once the `_resource_path` is defined, the resource class can define instance methods to expose -functionality related to the resource that it represents, and can use the `self.request` helper -method to make authenticated requests to API paths under the `_resource_path`. For example, -if `_resource_path` is set to `['users']`, then calling `self.request(method='POST')` would make -a `POST` request to `/api/v2/users`. (A more precise description of the `request` method is given -in the following section) - -With that in mind, most methods that the developer chooses to add to a resource class will probably -just be a very thin method that passes the appropriate arguments into `self.request`, and returns -the result. - - -#### The `request` Helper Method - -`self.request` is a helper method that handles the details of authentication, response parsing, and -pagination, such that the caller only needs to specify the API path, HTTP method, and request data. -The method arguments are as follows: +This project is now managed with `uv`, and exposes a cli tool to automate most maintenance tasks. +`uv run cli --help` for details. -- `path (optional)` Any additional path elements that should be added after the `_resource_path` -- `method (optional, default: 'GET')` The HTTP method -- `data (optional)` A python dict defining either the query params or the JSON payload, depending on - which HTTP method is specified -- `headers (optional)` Any additional headers that should be included in the request (the API key - is automatically included) -If the request succeeds, then `self.request` will either return a python dict, or an iterator of -python dicts, depending on whether the server responds with a pagenated response. Pagenated -responses will be detected automatically, so the caller does not need to worry about it. +### Maintenance Releases -If the request fails, then a `requests.HTTPError` exception will be raised, and it'll be up to the -consumer of this library to deal with it 😎 +Changes/additions to the Dialpad API can be handled (mostly) automatically 👍 +#### Update Procedure -#### The `resources/__init__.py` File +- Overwrite `dialpad_api_spec.json` with the latest spec -When a new file is added to the `resources` directory, a new import statement should also be added -to `__init__.py` to expose the newly defined resource class as a direct property of the `resources` -module. +- Run `uv run cli preprocess-spec` + - This just does a few ham-fisted inplace edits to the spec file to make the schema paths a bit nicer +- Run `uv run cli update-resource-module-mapping --interactive` + - This adds entries to `module_mapping.json` for any new API operations in the API spec. + We (humans) get to decide the appropriate resource class and method name 👍 -#### `DialpadClient` Resource Properties +![resource module mapping](./docs/images/resource_module_mapping.png) -In addition to adding the new class to the `__init__.py` file, the new resource class should also -be added as a cached property of the `DialpadClient` class. +- Run `uv run cli generate-client` + - This will regenerate all of the schema and resource files as per the API spec. +- Run `uv run pytest` + - Never hurts to confirm that nothing got borked 👍 -#### Recap +TODO: version bump, build, push, publish -To add a new resource to this client library, simply: -- Create a new file under the `resources` directory -- Define a new subclass of `DialpadResource` within said file -- Expose methods related to that resource as methods on your new class -- Add a new import statement in `resources/__init__.py` -- Add a new property to the `DialpadClient` class diff --git a/cli/__init__.py b/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/client_gen/__init__.py b/cli/client_gen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cli/client_gen/annotation.py b/cli/client_gen/annotation.py new file mode 100644 index 0000000..c33b7b9 --- /dev/null +++ b/cli/client_gen/annotation.py @@ -0,0 +1,226 @@ +import ast +from typing import Optional + +from jsonschema_path.paths import SchemaPath + +"""Utilities for converting OpenAPI schema pieces to Python type annotations.""" + + +def spec_type_to_py_type(s_type: str, s_format: Optional[str]) -> str: + """Converts an OpenAPI type+format to a Python type string""" + s_mapping = { + ('integer', None): 'int', + ('integer', 'int32'): 'int', + ('integer', 'int64'): 'int', + ('string', None): 'str', + ( + 'string', + 'byte', + ): "Annotated[str, 'base64']", + ( + 'string', + 'date-time', + ): 'str', # TODO: We could probably bake the ISO-str conversion into the client lib here too + ('boolean', None): 'bool', + ( + 'object', + None, + ): 'dict', # There are a few cases where there are genuine free-form dicts(such as app settings) + ('number', 'double'): 'float', + } + if (s_type, s_format) in s_mapping: + return s_mapping[(s_type, s_format)] + + raise NotImplementedError(f'Unhandled OpenAPI type: {s_type} (format: {s_format})') + + +def enum_to_py_type(enum_list: list) -> str: + """Converts an OpenAPI enum list to a Python type string""" + + literal_parts = [] + for val in enum_list: + if isinstance(val, str): + literal_parts.append(f"'{val}'") + elif isinstance(val, (int, float, bool)): + literal_parts.append(str(val)) + elif val is None: + literal_parts.append('None') + else: + raise NotImplementedError(f'Unhandled enum part: {val}') + + return f'Literal[{", ".join(literal_parts)}]' + + +def create_annotation(py_type: str, nullable: bool, omissible: bool) -> ast.Name: + """Creates an ast.Name annotation with the given name and type""" + id_str = py_type + + if nullable: + id_str = f'Optional[{id_str}]' + + if omissible: + id_str = f'NotRequired[{id_str}]' + + return ast.Name(id=id_str, ctx=ast.Load()) + + +def schema_dict_to_annotation( + schema_dict: dict, + override_nullable: Optional[bool] = None, + override_omissible: Optional[bool] = None, +) -> ast.Name: + """Converts a schema dict to the appropriate ast.Name annotation.""" + # If we've been given an explicit override, then we'll take it as canon. + nullable = override_nullable + + # If the override was not explicit, then we'll decide for ourselves. + if nullable is None: + nullable = schema_dict.get('nullable', False) and schema_dict.get('default', None) is None + + # Same deal with omissible. + omissible = override_omissible + if omissible is None: + omissible = False + + # Handle enums specially. + if 'enum' in schema_dict: + return create_annotation( + py_type=enum_to_py_type(schema_dict['enum']), nullable=nullable, omissible=omissible + ) + + # Same with '$ref' -- we want to treat this as an imported annotation type + if '$ref' in schema_dict: + return create_annotation( + py_type=schema_dict['$ref'].split('.')[-1], nullable=nullable, omissible=omissible + ) + + # Array types we'll need to be a bit careful with. + if schema_dict.get('type') == 'array': + # First we'll recurse on the inner type: + inner_type: ast.Name = schema_dict_to_annotation(schema_dict['items']) + + # Now we'll wrap that annotation type with `list` + return create_annotation( + py_type=f'list[{inner_type.id}]', nullable=nullable, omissible=omissible + ) + + # oneOfs also need to be handled specially. + if 'oneOf' in schema_dict: + inner_types = [ + schema_dict_to_annotation(one_of_schema) for one_of_schema in schema_dict['oneOf'] + ] + return create_annotation( + py_type=f'Union[{", ".join([inner_type.id for inner_type in inner_types])}]', + nullable=nullable, + omissible=omissible, + ) + + # Otherwise, we'll treat it as a simple type. + return create_annotation( + py_type=spec_type_to_py_type(schema_dict['type'], schema_dict.get('format', None)), + nullable=nullable, + omissible=omissible, + ) + + +def _is_collection_schema(schema_dict: dict) -> bool: + """ + Determines if a schema represents a collection (paginated response with items). + Returns True if the schema is an object with an 'items' property that's an array. + """ + if schema_dict.get('type') == 'object' and 'properties' in schema_dict: + properties = schema_dict.get('properties', {}) + if 'items' in properties and properties['items'].get('type') == 'array': + return True + return False + + +def _get_collection_item_type(schema_dict: dict) -> str: + """ + Extracts the item type from a collection schema. + For collections, returns the type of items in the array. + """ + if not _is_collection_schema(schema_dict): + return None + + # Get the items property schema (which is an array) + items_prop = schema_dict['properties']['items'] + + # Extract the item type from the array items schema + if 'items' in items_prop: + item_type_schema = items_prop['items'] + + # Handle '$ref' case - most common for collection items + if '$ref' in item_type_schema: + return item_type_schema['$ref'].split('.')[-1] + + # Handle other cases if needed + inner_type = schema_dict_to_annotation(item_type_schema) + return inner_type.id + + return None + + +def spec_piece_to_annotation(spec_piece: SchemaPath) -> ast.Name: + """Converts requestBody, responses, property, or parameter elements to the appropriate ast.Name annotation""" + spec_dict = spec_piece.contents() + + # Parameters are a bit special, so we'll handle those upfront. + if spec_piece.parts[-2] == 'parameters': + if spec_dict['in'] == 'path': + # Path parameters must be present, and must not be None. + return schema_dict_to_annotation( + spec_dict['schema'], + override_nullable=False, + override_omissible=False, + ) + + # Otherwise, we'll use 'required' to drive the annotation nullability + return schema_dict_to_annotation( + spec_dict['schema'], + override_nullable=not spec_dict['required'] if 'required' in spec_dict else None, + ) + + # Request bodies can also just defer to the content schema. + if spec_piece.parts[-1] == 'requestBody': + return schema_dict_to_annotation(spec_dict['content']['application/json']['schema']) + + # Responses are a bit special. If they have a 200, then we'll use that schema + # Otherwise, we'll assume the appropriate type is None. + if spec_piece.parts[-1] == 'responses': + if '200' in spec_dict: + # If there is no content schema... then we'll assume that None is the + # correct return type. + if 'content' not in spec_dict['200']: + return create_annotation(py_type='None', nullable=False, omissible=False) + + response_schema = spec_dict['200']['content']['application/json']['schema'] + + dereffed_response_schema = ( + spec_piece / '200' / 'content' / 'application/json' / 'schema' + ).contents() + + # Check if this is a collection response and modify the type accordingly + if _is_collection_schema(dereffed_response_schema): + item_type = _get_collection_item_type(dereffed_response_schema) + if item_type: + # Return Iterator[ItemType] instead of the Collection type + return create_annotation( + py_type=f'Iterator[{item_type}]', nullable=False, omissible=False + ) + + return schema_dict_to_annotation(response_schema) + + return create_annotation(py_type='None', nullable=False, omissible=False) + + # If this is a property, then it's (mostly) just a schema dict + if spec_piece.parts[-2] == 'properties': + # We need to be careful that we don't inadvertently traverse $ref here, so + # we need to do a cute little hack to retrieve this spec-piece's unresolved + # schema dict + with spec_piece.accessor.resolve(spec_piece.parts[:-1]) as p: + spec_dict = p.contents[spec_piece.parts[-1]] + + return schema_dict_to_annotation(spec_dict) + + raise NotImplementedError(f'Unhandled OpenAPI annotation for: {spec_dict}') diff --git a/cli/client_gen/module_mapping.py b/cli/client_gen/module_mapping.py new file mode 100644 index 0000000..1a147ea --- /dev/null +++ b/cli/client_gen/module_mapping.py @@ -0,0 +1,154 @@ +import json +import logging +import os + +from jsonschema_path.paths import SchemaPath +from rich.console import Console +from rich.panel import Panel +from rich.prompt import Prompt +from rich.text import Text +from typing_extensions import TypedDict + +logger = logging.getLogger(__name__) +console = Console() + +REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) +MAPPING_FILE = os.path.join(REPO_ROOT, 'module_mapping.json') + + +class ModuleMappingEntry(TypedDict): + """A single entry in the module mapping configuration.""" + + resource_class: str + """The resource class name that this API operation should map to.""" + + method_name: str + """The name of the method the that operation should map to.""" + + +def load_module_mapping() -> dict[str, dict[str, ModuleMappingEntry]]: + """Loads the resource module mapping from the configuration file.""" + + with open(MAPPING_FILE, 'r') as f: + return json.load(f) + + +def get_suggested_class_name( + current_mapping: dict[str, dict[str, ModuleMappingEntry]], api_path: str +) -> str: + """Gets a suggested class name for a resource class, given the current mapping and the relevant SchemaPath.""" + # Find the longest prefix match in the current mapping + longest_prefix = '' + matched_class_name = '' + + for path, methods in current_mapping.items(): + # Determine the common prefix between the current path and the API path + if api_path.startswith(path) and len(path) > len(longest_prefix): + longest_prefix = path + # Get the first method entry's class name (they should all be the same for a path) + if methods: + first_method = next(iter(methods.values())) + matched_class_name = first_method['resource_class'] + + if matched_class_name: + return matched_class_name + + # If no match is found, extract from non-parametric path elements + path_parts = [p for p in api_path.split('/') if p and not (p.startswith('{') and p.endswith('}'))] + + if not path_parts: + return 'RootResource' + + # Use the last non-parametric element as the base for the class name + last_part = path_parts[-1] + + # Convert to camel case and append "Resource" + class_name = ''.join(p.capitalize() for p in last_part.replace('-', '_').split('_')) + return f'{class_name}Resource' + + +def get_suggested_method_name( + current_mapping: dict[str, dict[str, ModuleMappingEntry]], api_path: str, http_method: str +) -> str: + """Gets a suggested method name for a resource class, given the current mapping and the relevant SchemaPath.""" + http_method = http_method.lower() + + # Check if the last path element is parametrized + path_parts = api_path.split('/') + last_part = path_parts[-1] if path_parts else '' + is_parametrized = last_part.startswith('{') and last_part.endswith('}') + + # Map HTTP methods to Python method names + if http_method == 'get': + return 'get' if is_parametrized else 'list' + elif http_method == 'post': + return 'create' + elif http_method == 'put': + return 'update' + elif http_method == 'patch': + return 'partial_update' + elif http_method == 'delete': + return 'delete' + else: + return http_method # For other HTTP methods, use as is + + +def update_module_mapping(api_spec: SchemaPath, interactive: bool = False): + """Updates the resource module mapping with any new paths and operations found in the OpenAPI spec.""" + module_mapping = load_module_mapping() + added_entries = [] + + for api_path, path_entry in (api_spec / 'paths').items(): + if api_path not in module_mapping: + module_mapping[api_path] = {} + + for http_method, method_entry in path_entry.items(): + # If the method already has an entry, then just move on. + if http_method in module_mapping[api_path]: + continue + + suggested_class_name = get_suggested_class_name(module_mapping, api_path) + suggested_method_name = get_suggested_method_name(module_mapping, api_path, http_method) + + if interactive: + console.print('\n\n') + console.print( + Panel( + f'[bold]New API endpoint:[/bold] {api_path} [{http_method.upper()}]', + subtitle=method_entry.contents().get('summary', 'No summary available'), + ) + ) + + # Prompt for class name + class_name_prompt = Text() + class_name_prompt.append('Resource class name: ') + class_name_prompt.append(f'(default: {suggested_class_name})', style='dim') + resource_class = Prompt.ask(class_name_prompt, default=suggested_class_name) + + # Prompt for method name + method_name_prompt = Text() + method_name_prompt.append('Method name: ') + method_name_prompt.append(f'(default: {suggested_method_name})', style='dim') + method_name = Prompt.ask(method_name_prompt, default=suggested_method_name) + else: + resource_class = suggested_class_name + method_name = suggested_method_name + + # Add the entry to the module mapping + module_mapping[api_path][http_method] = { + 'resource_class': resource_class, + 'method_name': method_name, + } + + added_entries.append((api_path, http_method, resource_class, method_name)) + + # Save the updated mapping back to the file + with open(MAPPING_FILE, 'w') as f: + json.dump(module_mapping, f, indent=2) + + if added_entries: + console.print(f'[green]Added {len(added_entries)} new mapping entries:[/green]') + for api_path, http_method, resource_class, method_name in added_entries: + console.print(f' {api_path} [{http_method.upper()}] -> {resource_class}.{method_name}') + else: + console.print('[green]No new mapping entries needed.[/green]') diff --git a/cli/client_gen/resource_classes.py b/cli/client_gen/resource_classes.py new file mode 100644 index 0000000..ac492f1 --- /dev/null +++ b/cli/client_gen/resource_classes.py @@ -0,0 +1,135 @@ +import ast +import logging +from typing import List, Tuple + +from jsonschema_path.paths import SchemaPath + +from .resource_methods import http_method_to_func_def + +"""Utilities for converting OpenAPI schema pieces to Python Resource class definitions.""" + +logger = logging.getLogger(__name__) +VALID_HTTP_METHODS = {'get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'} + + +def resource_class_to_class_def( + class_name: str, operations_list: List[Tuple[SchemaPath, str, str]] +) -> ast.ClassDef: + """ + Converts a list of OpenAPI operations to a Python resource class definition. + + Args: + class_name: The name of the resource class (e.g., 'UsersResource') + operations_list: List of (operation_spec_path, target_method_name, original_api_path) tuples for this class + + Returns: + An ast.ClassDef node representing the Python resource class + """ + class_body_stmts: list[ast.stmt] = [] + + # Class Docstring + class_docstring_parts = [f'{class_name} resource class'] + + # Add a list of API paths this resource handles + api_paths = sorted(set(api_path for _, _, api_path in operations_list)) + if api_paths: + class_docstring_parts.append('') + class_docstring_parts.append('Handles API operations for:') + for path in api_paths: + class_docstring_parts.append(f'- {path}') + + final_class_docstring = '\n'.join(class_docstring_parts) + class_body_stmts.append(ast.Expr(value=ast.Constant(value=final_class_docstring))) + + # Generate methods for each operation + for operation_spec_path, target_method_name, original_api_path in sorted( + operations_list, + key=lambda x: x[1], # Sort by target method name + ): + try: + # Get the HTTP method (e.g., GET, POST) from the operation path + http_method = operation_spec_path.parts[-1].lower() + if http_method not in VALID_HTTP_METHODS: + logger.warning(f'Skipping operation with invalid HTTP method: {http_method}') + continue + + # Generate function definition for this operation + func_def = http_method_to_func_def( + operation_spec_path, override_func_name=target_method_name, api_path=original_api_path + ) + class_body_stmts.append(func_def) + except Exception as e: + logger.error(f'Error generating function for {target_method_name}: {e}') + + # Base class: DialpadResource + base_class_node = ast.Name(id='DialpadResource', ctx=ast.Load()) + + return ast.ClassDef( + name=class_name, bases=[base_class_node], keywords=[], body=class_body_stmts, decorator_list=[] + ) + + +# Keep the old function for backward compatibility or testing +def _path_str_to_class_name(path_str: str) -> str: + """Converts an OpenAPI path string to a Python class name.""" + if path_str == '/': + return 'RootResource' + + name_parts = [] + cleaned_path = path_str.lstrip('/') + for part in cleaned_path.split('/'): + if part.startswith('{') and part.endswith('}'): + param_name = part[1:-1] + # Convert snake_case or kebab-case to CamelCase (e.g., user_id -> UserId) + name_parts.append(''.join(p.capitalize() for p in param_name.replace('-', '_').split('_'))) + else: + # Convert static part to CamelCase (e.g., call-queues -> CallQueues) + name_parts.append(''.join(p.capitalize() for p in part.replace('-', '_').split('_'))) + + return ''.join(name_parts) + 'Resource' + + +def resource_path_to_class_def(resource_path: SchemaPath) -> ast.ClassDef: + """ + Converts an OpenAPI resource path to a Python resource class definition. + + DEPRECATED: Use resource_class_to_class_def instead. + """ + path_item_dict = resource_path.contents() + path_key = resource_path.parts[-1] # The actual path string, e.g., "/users/{id}" + + class_name = _path_str_to_class_name(path_key) + + class_body_stmts: list[ast.stmt] = [] + + # Class Docstring + class_docstring_parts = [] + summary = path_item_dict.get('summary') + description = path_item_dict.get('description') + + if summary: + class_docstring_parts.append(summary) + if description: + if summary: # Add a blank line if summary was also present + class_docstring_parts.append('') + class_docstring_parts.append(description) + + if not class_docstring_parts: + class_docstring_parts.append(f'Resource for the path {path_key}') + + final_class_docstring = '\n'.join(class_docstring_parts) + class_body_stmts.append(ast.Expr(value=ast.Constant(value=final_class_docstring))) + + # Methods for HTTP operations + for http_method_name in path_item_dict.keys(): + if http_method_name.lower() in VALID_HTTP_METHODS: + method_spec_path = resource_path / http_method_name + func_def = http_method_to_func_def(method_spec_path) + class_body_stmts.append(func_def) + + # Base class: DialpadResource + base_class_node = ast.Name(id='DialpadResource', ctx=ast.Load()) + + return ast.ClassDef( + name=class_name, bases=[base_class_node], keywords=[], body=class_body_stmts, decorator_list=[] + ) diff --git a/cli/client_gen/resource_methods.py b/cli/client_gen/resource_methods.py new file mode 100644 index 0000000..ff904fa --- /dev/null +++ b/cli/client_gen/resource_methods.py @@ -0,0 +1,372 @@ +import ast +import re +from typing import Optional + +from jsonschema_path.paths import SchemaPath + +from .annotation import spec_piece_to_annotation + +"""Utilities for converting OpenAPI schema pieces to Python Resource method definitions.""" + + +def http_method_to_func_name(method_spec: SchemaPath) -> str: + """ + Converts the HTTP method in the path to the Python function name. + This will be the lowercase HTTP method name (get, post, delete, etc.) + """ + return method_spec.parts[-1].lower() + + +def _is_collection_response(method_spec: SchemaPath) -> bool: + """ + Determines if a method response is a collection (array) that should use iter_request. + Returns True if the 200 response is an object with an 'items' property that's an array. + """ + response_type = spec_piece_to_annotation(method_spec / 'responses') + return response_type.id.startswith('Iterator[') + + +def _build_method_call_args( + method_spec: SchemaPath, api_path: Optional[str] = None +) -> list[ast.expr]: + """ + Build the argument expressions for the request/iter_request method call. + + Args: + method_spec: The SchemaPath for the operation + api_path: The original API path string (e.g., '/users/{user_id}') + + Returns: + List of ast.expr nodes to pass as arguments to self._request or self._iter_request + """ + # Get HTTP method name (GET, POST, etc.) + http_method = method_spec.parts[-1].upper() + + # Create method argument + method_arg = ast.keyword(arg='method', value=ast.Constant(value=http_method)) + + args = [method_arg] + + # Handle sub_path based on API path + if api_path and '{' in api_path: + # This is a path with parameters that needs formatting + # We'll need to create a formatted string as the sub_path + + # Parse the path into alternating constant and parameter parts + # For '/users/{user_id}/posts/{post_id}' we get: + # constants: ['/users/', '/posts/', ''] + # params: ['user_id', 'post_id'] + parts = re.split(r'\{([^}]+)\}', api_path) + + # Create the f-string AST values by alternating constants and formatted values + fstring_values = [] + for i, part in enumerate(parts): + if i % 2 == 0: + # Even indices are constant string parts + if part: # Only add non-empty constants + fstring_values.append(ast.Constant(value=part)) + else: + # Odd indices are parameter names + fstring_values.append( + ast.FormattedValue( + value=ast.Name(id=part, ctx=ast.Load()), + conversion=-1, # No conversion specified + format_spec=None, + ) + ) + + sub_path_arg = ast.keyword( + arg='sub_path', + value=ast.JoinedStr(values=fstring_values), + ) + elif api_path: + # Fixed path, no parameters + sub_path_arg = ast.keyword(arg='sub_path', value=ast.Constant(value=api_path)) + else: + # No API path provided + sub_path_arg = None + + if sub_path_arg: + args.append(sub_path_arg) + + # Collect parameters for the request + param_spec_paths = [] + if 'parameters' in method_spec: + parameters_list_path = method_spec / 'parameters' + if isinstance(parameters_list_path.contents(), list): + param_spec_paths = list(parameters_list_path) + + # Process query parameters + query_params = [p for p in param_spec_paths if p['in'] == 'query'] + if query_params: + # Create a params dictionary for the query parameters + params_dict_elements = [] + for param in query_params: + param_name = param['name'] + # Only include the parameter if it's not None + params_dict_elements.append( + ast.IfExp( + test=ast.Compare( + left=ast.Name(id=param_name, ctx=ast.Load()), + ops=[ast.IsNot()], + comparators=[ast.Constant(value=None)], + ), + body=ast.Tuple( + elts=[ast.Constant(value=param_name), ast.Name(id=param_name, ctx=ast.Load())], + ctx=ast.Load(), + ), + orelse=ast.Constant(value=None), + ) + ) + + # Create params argument with dictionary comprehension filtering out None values + params_arg = ast.keyword( + arg='params', + value=ast.Dict( + keys=[ast.Constant(value=p['name']) for p in query_params], + values=[ast.Name(id=p['name'], ctx=ast.Load()) for p in query_params], + ), + ) + args.append(params_arg) + + # Add request body if present + has_request_body = 'requestBody' in method_spec.contents() + if has_request_body: + body_arg = ast.keyword(arg='body', value=ast.Name(id='request_body', ctx=ast.Load())) + args.append(body_arg) + + return args + + +def http_method_to_func_body( + method_spec: SchemaPath, api_path: Optional[str] = None +) -> list[ast.stmt]: + """ + Generates the body of the Python function, including a docstring and request call. + + Args: + method_spec: The SchemaPath for the operation + api_path: The original API path string (e.g., '/users/{user_id}') + + Returns: + A list of ast.stmt nodes representing the function body + """ + docstring_parts = [] + + # Operation summary and description + summary = method_spec.contents().get('summary') + description = method_spec.contents().get('description') + operation_id = method_spec.contents().get('operationId') + + if summary: + docstring_parts.append(summary) + elif operation_id: # Fallback to operationId if summary is not present + docstring_parts.append(f'Corresponds to operationId: {operation_id}') + + if description: + if summary: # Add a blank line if summary was also present + docstring_parts.append('') + docstring_parts.append(description) + + # Args section + args_doc_lines = [] + + # Collect parameters + param_spec_paths = [] + if 'parameters' in method_spec: + parameters_list_path = method_spec / 'parameters' + if isinstance(parameters_list_path.contents(), list): + param_spec_paths = list(parameters_list_path) + + # Path parameters + path_param_specs = sorted( + [p for p in param_spec_paths if p['in'] == 'path'], key=lambda p: p['name'] + ) + for p_spec in path_param_specs: + param_name = p_spec['name'] + param_desc = p_spec.contents().get('description', 'No description available.') + args_doc_lines.append(f' {param_name}: {param_desc}') + + # Query parameters + query_param_specs = sorted( + [p for p in param_spec_paths if p['in'] == 'query'], key=lambda p: p['name'] + ) + for p_spec in query_param_specs: + param_name = p_spec['name'] + param_desc = p_spec.contents().get('description', 'No description available.') + args_doc_lines.append(f' {param_name}: {param_desc}') + + # Request body + request_body_path = method_spec / 'requestBody' + if request_body_path.exists(): + rb_desc = request_body_path.contents().get('description', 'The request body.') + args_doc_lines.append(f' request_body: {rb_desc}') + + if args_doc_lines: + if docstring_parts: # Add a blank line if summary/description was present + docstring_parts.append('') + docstring_parts.append('Args:') + docstring_parts.extend(args_doc_lines) + + # Returns section + responses_path = method_spec / 'responses' + if '200' in responses_path: + resp_200 = responses_path / '200' + if 'description' in resp_200: + desc_200 = resp_200.contents()['description'] + if docstring_parts: + docstring_parts.append('') + docstring_parts.append('Returns:') + + # Check if this is a collection response + is_collection = _is_collection_response(method_spec) + if is_collection: + # Update the return description to indicate it's an iterator + docstring_parts.append(f' An iterator of items from {desc_200}') + else: + docstring_parts.append(f' {desc_200}') + + # Construct the final docstring string + final_docstring = '\n'.join(docstring_parts) if docstring_parts else 'No description available.' + + # Create docstring node + docstring_node = ast.Expr(value=ast.Constant(value=final_docstring)) + + # Determine if this is a collection response method + is_collection = _is_collection_response(method_spec) + + # Build method call arguments + call_args = _build_method_call_args(method_spec, api_path=api_path) + + # Create the appropriate request method call + method_name = '_iter_request' if is_collection else '_request' + + request_call = ast.Return( + value=ast.Call( + func=ast.Attribute( + value=ast.Name(id='self', ctx=ast.Load()), attr=method_name, ctx=ast.Load() + ), + args=[], + keywords=call_args, + ) + ) + + # Put it all together + return [docstring_node, request_call] + + +def _get_python_default_value_ast(param_spec_path: SchemaPath) -> ast.expr: + """ + Determines the AST for a default value of an optional parameter. + An optional parameter in OpenAPI (required: false) can have a 'default' + value specified in its schema. If so, that's the Python default. + Otherwise, the Python default is None. + """ + # param_spec_path points to the parameter object (e.g., .../parameters/0). + # The default value is in param_spec_path -> schema -> default. + schema_path = param_spec_path / 'schema' + default_value_path = schema_path / 'default' + if default_value_path.exists(): + default_value = default_value_path.contents() + return ast.Constant(value=default_value) + return ast.Constant(value=None) + + +def http_method_to_func_args(method_spec: SchemaPath) -> ast.arguments: + """Converts OpenAPI method parameters and requestBody to Python function arguments.""" + python_func_args = [ast.arg(arg='self', annotation=None)] + python_func_defaults = [] + + # Collect all parameter SchemaPath objects + param_spec_paths = [] + if 'parameters' in method_spec: + parameters_list_path = method_spec / 'parameters' + # Ensure parameters_list_path is iterable (it is if it points to a list) + if isinstance(parameters_list_path.contents(), list): + param_spec_paths = list(parameters_list_path) + + # Path parameters (always required, appear first after self) + path_param_specs = sorted( + [p for p in param_spec_paths if p['in'] == 'path'], key=lambda p: p['name'] + ) + for p_spec in path_param_specs: + python_func_args.append( + ast.arg(arg=p_spec['name'], annotation=spec_piece_to_annotation(p_spec)) + ) + + # Query parameters + query_param_specs = sorted( + [p for p in param_spec_paths if p['in'] == 'query'], key=lambda p: p['name'] + ) + + # Required query parameters + required_query_specs = [p for p in query_param_specs if p.contents().get('required', False)] + for p_spec in required_query_specs: + python_func_args.append( + ast.arg(arg=p_spec['name'], annotation=spec_piece_to_annotation(p_spec)) + ) + + # Request body + request_body_path = method_spec / 'requestBody' + has_request_body = request_body_path.exists() + # Technically this should default to False... but we assume the opposite on the server side. + is_request_body_required = has_request_body and request_body_path.contents().get('required', True) + + # Required request body + if has_request_body and is_request_body_required: + python_func_args.append( + ast.arg(arg='request_body', annotation=spec_piece_to_annotation(request_body_path)) + ) + + # Optional query parameters (these will have defaults) + optional_query_specs = [p for p in query_param_specs if not p.contents().get('required', False)] + for p_spec in optional_query_specs: + python_func_args.append( + ast.arg(arg=p_spec['name'], annotation=spec_piece_to_annotation(p_spec)) + ) + python_func_defaults.append(_get_python_default_value_ast(p_spec)) + + # Optional request body (will have a default of None) + if has_request_body and not is_request_body_required: + python_func_args.append( + ast.arg(arg='request_body', annotation=spec_piece_to_annotation(request_body_path)) + ) + python_func_defaults.append(ast.Constant(value=None)) + + return ast.arguments( + posonlyargs=[], + args=python_func_args, + vararg=None, + kwonlyargs=[], + kw_defaults=[], + kwarg=None, + defaults=python_func_defaults, + ) + + +def http_method_to_func_def( + method_spec: SchemaPath, override_func_name: Optional[str] = None, api_path: Optional[str] = None +) -> ast.FunctionDef: + """ + Converts an OpenAPI method spec to a Python function definition. + + Args: + method_spec: The SchemaPath for the operation + override_func_name: An optional name to use for the function instead of the default + api_path: The original API path string (e.g., '/users/{user_id}') + + Returns: + An ast.FunctionDef node representing the Python method + """ + func_name = override_func_name if override_func_name else http_method_to_func_name(method_spec) + + # Generate function body with potentially modified path + func_body = http_method_to_func_body(method_spec, api_path=api_path) + + return ast.FunctionDef( + name=func_name, + args=http_method_to_func_args(method_spec), + body=func_body, + decorator_list=[], + returns=spec_piece_to_annotation(method_spec / 'responses'), + ) diff --git a/cli/client_gen/resource_modules.py b/cli/client_gen/resource_modules.py new file mode 100644 index 0000000..161d162 --- /dev/null +++ b/cli/client_gen/resource_modules.py @@ -0,0 +1,149 @@ +import ast +from typing import Dict, List, Optional, Set, Tuple + +from jsonschema_path.paths import SchemaPath + +from .resource_classes import resource_class_to_class_def +from .resource_methods import _is_collection_response + +"""Utilities for converting OpenAPI schema pieces to Python Resource modules.""" + + +def _ref_value_to_import_path(ref_value: str) -> Optional[Tuple[str, str]]: + # Extract the schema name from the reference + if ref_value.startswith('#/components/schemas/'): + schema_name = ref_value.split('/')[-1] + + # Convert schema name to import path + # e.g., "schemas.targets.office.OfficeSchema" → "dialpad.schemas.targets.office", "OfficeSchema" + parts = schema_name.split('.') + if len(parts) > 1: + import_path = 'dialpad.' + '.'.join(parts[:-1]) + class_name = parts[-1] + return import_path, class_name + + +def _extract_schema_dependencies( + operations_list: List[Tuple[SchemaPath, str, str]], +) -> Dict[str, Set[str]]: + """ + Extract schema dependencies from a list of operations that need to be imported. + + Args: + operations_list: List of (operation_spec_path, target_method_name, api_path) tuples + + Returns: + A dictionary mapping import paths to sets of schema names to import from that path. + """ + imports_needed: Dict[str, Set[str]] = {} + + # Helper function to scan for schema references in a dict + def scan_for_refs(obj: dict) -> None: + if not isinstance(obj, dict): + return + + # Check if this is a $ref to a schema + if '$ref' in obj and isinstance(obj['$ref'], str): + ref_value = obj['$ref'] + import_tuple = _ref_value_to_import_path(ref_value) + if import_tuple: + import_path, class_name = import_tuple + + # Add to imports mapping + if import_path not in imports_needed: + imports_needed[import_path] = set() + imports_needed[import_path].add(class_name) + + # Recursively check all dictionary values + for value in obj.values(): + if isinstance(value, dict): + scan_for_refs(value) + elif isinstance(value, list): + for item in value: + if isinstance(item, dict): + scan_for_refs(item) + + # Scan all operations in the list + for operation_spec_path, _, _ in operations_list: + # Get the operation spec contents + operation_dict = operation_spec_path.contents() + if isinstance(operation_dict, dict): + scan_for_refs(operation_dict) + + # Special-case collection responses so that we import the inner type rather than the collection + # type. + if 'responses' not in operation_spec_path: + continue + + if _is_collection_response(operation_spec_path): + dereffed_response_schema = ( + operation_spec_path / 'responses' / '200' / 'content' / 'application/json' / 'schema' + ).contents() + item_ref_value = dereffed_response_schema['properties']['items']['items']['$ref'] + item_import_tuple = _ref_value_to_import_path(item_ref_value) + if item_import_tuple: + item_import_path, item_class_name = item_import_tuple + if item_import_path not in imports_needed: + imports_needed[item_import_path] = set() + imports_needed[item_import_path].add(item_class_name) + + return imports_needed + + +def resource_class_to_module_def( + class_name: str, operations_list: List[Tuple[SchemaPath, str, str]], api_spec: SchemaPath +) -> ast.Module: + """ + Converts a resource class specification to a Python module definition (ast.Module). + + Args: + class_name: The name of the resource class (e.g., 'UsersResource') + operations_list: List of (operation_spec_path, target_method_name, api_path) tuples for this class + api_spec: The full API spec SchemaPath (for context) + + Returns: + An ast.Module containing the resource class definition with all operations + """ + # Extract schema dependencies for imports + schema_dependencies = _extract_schema_dependencies(operations_list) + + # Create import statements list, starting with the base resource import + import_statements = [ + # Add typing imports that might be needed for method signatures + ast.ImportFrom( + module='typing', + names=[ + ast.alias(name='Optional', asname=None), + ast.alias(name='List', asname=None), + ast.alias(name='Dict', asname=None), + ast.alias(name='Union', asname=None), + ast.alias(name='Literal', asname=None), + ast.alias(name='Iterator', asname=None), + ast.alias(name='Any', asname=None), + ], + level=0, # Absolute import + ), + ast.ImportFrom( + module='dialpad.resources.base', + names=[ast.alias(name='DialpadResource', asname=None)], + level=0, # Absolute import + ), + ] + + # Add imports for schema dependencies + for import_path, schema_names in sorted(schema_dependencies.items()): + import_statements.append( + ast.ImportFrom( + module=import_path, + names=[ast.alias(name=name, asname=None) for name in sorted(schema_names)], + level=0, # Absolute import + ) + ) + + # Generate the class definition using resource_class_to_class_def + class_definition = resource_class_to_class_def(class_name, operations_list) + + # Construct the ast.Module with imports and class definition + module_body = import_statements + [class_definition] + + return ast.Module(body=module_body, type_ignores=[]) diff --git a/cli/client_gen/resource_packages.py b/cli/client_gen/resource_packages.py new file mode 100644 index 0000000..c6b412e --- /dev/null +++ b/cli/client_gen/resource_packages.py @@ -0,0 +1,182 @@ +"""Orchestrates the generation of Python resource modules based on module_mapping.json.""" + +import os +import re # Ensure re is imported if to_snake_case is defined here or called +from typing import Dict, List, Tuple + +import rich +from jsonschema_path import SchemaPath +from rich.markdown import Markdown + +from .module_mapping import ModuleMappingEntry, load_module_mapping +from .resource_modules import resource_class_to_module_def +from .utils import reformat_python_file, write_python_file + + +def to_snake_case(name: str) -> str: + """Converts a CamelCase or PascalCase string to snake_case.""" + if not name: + return '' + + if name.endswith('Resource'): + name_part = name[: -len('Resource')] + if not name_part: # Original name was "Resource" + return 'resource_base' # Or some other default to avoid just "_resource" + # Convert the name part and add _resource suffix + converted = _convert_to_snake_case(name_part) + return f'{converted}_resource' if converted else 'base_resource' + + return _convert_to_snake_case(name) + + +def _convert_to_snake_case(name: str) -> str: + """Helper function to convert a string to snake_case with proper acronym handling.""" + # Handle sequences of uppercase letters followed by lowercase (like "XMLParser" -> "xml_parser") + words = re.findall(r'([A-Z]+[^A-Z]*)', name) + if not words: + return name.lower() # If no uppercase letters, just return the name in lowercase + + # Join the words with underscores and convert to lowercase + return '_'.join(word.lower() for word in words).strip('_') + + +def _group_operations_by_class( + api_spec: SchemaPath, module_mapping: Dict[str, Dict[str, ModuleMappingEntry]] +) -> Dict[str, List[Tuple[SchemaPath, str, str]]]: + """ + Groups API operations by their target resource class. + + Returns: + A dictionary where keys are resource class names and values are lists of + (operation_spec_path, http_method_lower, original_api_path_string). + """ + grouped_operations: Dict[str, List[Tuple[SchemaPath, str, str]]] = {} + openapi_paths = api_spec / 'paths' + + for api_path_str, path_item_spec_path in openapi_paths.items(): + path_item_contents = path_item_spec_path.contents() + if not isinstance(path_item_contents, dict): + continue + + for http_method, operation_spec_contents in path_item_contents.items(): + http_method_lower = http_method.lower() + if http_method_lower not in [ + 'get', + 'put', + 'post', + 'delete', + 'patch', + 'options', + 'head', + 'trace', + ]: + continue + if not isinstance(operation_spec_contents, dict): + continue + + operation_spec_path = path_item_spec_path / http_method + + path_mapping = module_mapping.get(api_path_str) + if not path_mapping: + # print(f"Warning: API path '{api_path_str}' not found in module mapping. Skipping.") + continue + + operation_mapping_entry = path_mapping.get(http_method_lower) + if not operation_mapping_entry: + # print(f"Warning: Method '{http_method_lower}' for path '{api_path_str}' not found in module mapping. Skipping.") + continue + + resource_class_name = operation_mapping_entry['resource_class'] + + if resource_class_name not in grouped_operations: + grouped_operations[resource_class_name] = [] + + grouped_operations[resource_class_name].append( + (operation_spec_path, http_method_lower, api_path_str) + ) + + return grouped_operations + + +def resources_to_package_directory( + api_spec: SchemaPath, + output_dir: str, +) -> None: + """ + Converts OpenAPI operations to a Python resource package directory structure, + grouping operations into classes based on module_mapping.json. + """ + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + init_file_path = os.path.join(output_dir, '__init__.py') + all_resource_class_names_in_package = [] + + try: + mapping_data = load_module_mapping() + except Exception as e: + print(f'Error loading module mapping: {e}') + return + + grouped_operations_by_class_name = _group_operations_by_class(api_spec, mapping_data) + + generated_module_snake_names = [] + + for resource_class_name, operations_for_class in grouped_operations_by_class_name.items(): + operations_with_target_methods = [] + for op_spec_path, http_method, original_api_path in operations_for_class: + target_method_name = mapping_data[original_api_path][http_method]['method_name'] + operations_with_target_methods.append((op_spec_path, target_method_name, original_api_path)) + + module_ast = resource_class_to_module_def( + resource_class_name, operations_with_target_methods, api_spec + ) + + module_file_snake_name = to_snake_case(resource_class_name) + module_file_path = os.path.join(output_dir, f'{module_file_snake_name}.py') + write_python_file(module_file_path, module_ast) + + generated_module_snake_names.append(module_file_snake_name) + all_resource_class_names_in_package.append(resource_class_name) + + with open(init_file_path, 'w') as f: + f.write('# This is an auto-generated resource package. Please do not edit it directly.\n\n') + + # Create a mapping from snake_case module name to its original ClassName + # to ensure correct import statements in __init__.py + class_name_map = {to_snake_case(name): name for name in all_resource_class_names_in_package} + + for module_snake_name in sorted(generated_module_snake_names): + actual_class_name = class_name_map.get(module_snake_name) + if actual_class_name: + f.write(f'from .{module_snake_name} import {actual_class_name}\n') + + # Add the DialpadResourcesMixin class + f.write('\n\nclass DialpadResourcesMixin:\n') + f.write(' """Mixin class that provides resource properties for each API resource.\n\n') + f.write(' This mixin is used by the DialpadClient class to provide easy access\n') + f.write(' to all API resources as properties.\n """\n\n') + + # Add a property for each resource class + for class_name in sorted(all_resource_class_names_in_package): + # Convert the class name to property name (removing 'Resource' suffix and converting to snake_case) + property_name = to_snake_case(class_name.removesuffix('Resource')) + + f.write(' @property\n') + f.write(f' def {property_name}(self) -> {class_name}:\n') + f.write(f' """Returns an instance of {class_name}.\n\n') + f.write(' Returns:\n') + f.write(f' A {class_name} instance initialized with this client.\n') + f.write(' """\n') + f.write(f' return {class_name}(self)\n\n') + + # Add __all__ for export of the classes and the mixin + f.write('\n__all__ = [\n') + for class_name in sorted(all_resource_class_names_in_package): + f.write(f" '{class_name}',\n") + f.write(" 'DialpadResourcesMixin',\n") + f.write(']\n') + + reformat_python_file(init_file_path) + + rich.print(Markdown(f'Resource package generated at `{output_dir}`.')) diff --git a/cli/client_gen/schema_classes.py b/cli/client_gen/schema_classes.py new file mode 100644 index 0000000..8ea4291 --- /dev/null +++ b/cli/client_gen/schema_classes.py @@ -0,0 +1,103 @@ +import ast +from typing import List, Set, Tuple + +from jsonschema_path.paths import SchemaPath + +from . import annotation + +"""Utilities for converting OpenAPI object schemas into TypedDict definitions.""" + + +def _extract_schema_title(object_schema: SchemaPath) -> str: + """Extracts the title from a schema, generating a default if not present.""" + return object_schema.parts[-1].split('.')[-1] + + +def _get_property_fields( + object_schema: SchemaPath, required_props: Set[str] +) -> List[Tuple[str, ast.expr, str]]: + """ + Extract property fields from schema and create appropriate annotations. + + Returns a list of (field_name, annotation, description) tuples. + """ + schema_dict = object_schema.contents() + fields = [] + + # Get properties from schema + if 'properties' not in schema_dict: + return fields + + for prop_name, prop_dict in schema_dict['properties'].items(): + # Determine if property is required + is_required = prop_name in required_props + + # Use schema_dict_to_annotation with appropriate flags + annotation_expr = annotation.schema_dict_to_annotation( + prop_dict, + override_nullable=False, # The vast majority of properties are improperly marked as nullable + override_omissible=not is_required, + ) + + # Get the field description from the spec + description = prop_dict.get('description', '') + + fields.append((prop_name, annotation_expr, description)) + + return fields + + +def schema_to_typed_dict_def(object_schema: SchemaPath) -> ast.ClassDef: + """Converts an OpenAPI object schema to a TypedDict definition (ast.ClassDef).""" + schema_dict = object_schema.contents() + + # Get class name from schema title + class_name = _extract_schema_title(object_schema) + + # Get required properties (default to empty list if not specified) + required_props = set(schema_dict.get('required', [])) + + # Extract property fields + field_items = _get_property_fields(object_schema, required_props) + + # Create class body + class_body = [] + + # Add docstring from schema description or title + docstring = schema_dict.get('description', '') + if not docstring and 'title' in schema_dict: + docstring = schema_dict['title'] + + # If no description available, provide a generic docstring + if not docstring: + docstring = f'TypedDict representation of the {class_name} schema.' + + # Add docstring as first element in class body + class_body.append(ast.Expr(value=ast.Constant(value=docstring))) + + # Add class annotations for each field along with field descriptions as string literals + for field_name, field_type, field_description in field_items: + # Add the field annotation + class_body.append( + ast.AnnAssign( + target=ast.Name(id=field_name, ctx=ast.Store()), annotation=field_type, value=None, simple=1 + ) + ) + + # Only add field description if it's not empty + if field_description: + # Add field description as a string literal right after the field annotation + # This is not standard, but VSCode will interpret it as a field docstring + class_body.append(ast.Expr(value=ast.Constant(value=field_description))) + + # If no fields were found, add a pass statement to avoid syntax error + if len(class_body) == 1: # Only the docstring is present + class_body.append(ast.Pass()) + + # Create the TypedDict base class + typed_dict_base = ast.Name(id='TypedDict', ctx=ast.Load()) + + # Create class definition with TypedDict inheritance + return ast.ClassDef( + name=class_name, bases=[typed_dict_base], keywords=[], body=class_body, decorator_list=[] + ) diff --git a/cli/client_gen/schema_modules.py b/cli/client_gen/schema_modules.py new file mode 100644 index 0000000..758a87a --- /dev/null +++ b/cli/client_gen/schema_modules.py @@ -0,0 +1,206 @@ +import ast +from typing import Dict, List, Set + +from jsonschema_path.paths import SchemaPath + +from .schema_classes import schema_to_typed_dict_def + + +def _extract_schema_title(schema: SchemaPath) -> str: + """Extracts the title from a schema.""" + return schema.parts[-1] + + +def _find_schema_dependencies(schema: SchemaPath) -> Set[str]: + """ + Find all schema names that this schema depends on through references. + Returns a set of schema titles that this schema depends on. + """ + dependencies = set() + schema_dict = schema.contents() + + # Helper function to recursively scan for $ref values + def scan_for_refs(obj: dict) -> None: + if not isinstance(obj, dict): + return + + # Check if this is a $ref + if '$ref' in obj and isinstance(obj['$ref'], str): + ref_value = obj['$ref'] + # Extract the schema name from the reference + # Assuming references are in format "#/components/schemas/SchemaName" + if ref_value.startswith('#/components/schemas/'): + schema_name = ref_value.split('/')[-1] + dependencies.add(schema_name) + + # Recursively check all dictionary values + for value in obj.values(): + if isinstance(value, dict): + scan_for_refs(value) + elif isinstance(value, list): + for item in value: + if isinstance(item, dict): + scan_for_refs(item) + + # Start scanning the schema + scan_for_refs(schema_dict) + return dependencies + + +def _extract_external_dependencies(schemas: List[SchemaPath]) -> Dict[str, Set[str]]: + """ + Extract external dependencies that need to be imported. + + Returns a dictionary mapping import paths to sets of schema names to import from that path. + """ + # Get all schema names in the current module + local_schema_titles = {_extract_schema_title(schema) for schema in schemas} + + # Map to collect import paths and their schemas + imports_needed: Dict[str, Set[str]] = {} + + for schema in schemas: + schema_dict = schema.contents() + + # Helper function to scan for external references + def scan_for_external_refs(obj: dict) -> None: + if not isinstance(obj, dict): + return + + # Check if this is a $ref to an external schema + if '$ref' in obj and isinstance(obj['$ref'], str): + ref_value = obj['$ref'] + # Extract the schema name from the reference + if ref_value.startswith('#/components/schemas/'): + schema_name = ref_value.split('/')[-1] + + # If the schema is not local to this module, we need to import it + if schema_name not in local_schema_titles: + # Convert schema name to import path + # e.g., "schemas.targets.office.OfficeSchema" → "dialpad.schemas.targets.office", "OfficeSchema" + parts = schema_name.split('.') + if len(parts) > 1: + import_path = 'dialpad.' + '.'.join(parts[:-1]) + class_name = parts[-1] + + # Add to imports mapping + if import_path not in imports_needed: + imports_needed[import_path] = set() + imports_needed[import_path].add(class_name) + + # Recursively check all dictionary values + for value in obj.values(): + if isinstance(value, dict): + scan_for_external_refs(value) + elif isinstance(value, list): + for item in value: + if isinstance(item, dict): + scan_for_external_refs(item) + + # Start scanning + scan_for_external_refs(schema_dict) + + return imports_needed + + +def _sort_schemas(schemas: List[SchemaPath]) -> List[SchemaPath]: + """ + Sort schemas to ensure dependencies are defined before they are referenced. + Uses topological sort based on schema dependencies with deterministic ordering. + """ + # Start with a pre-sorted list to ensure a consistent result order + schemas = list(sorted(schemas, key=_extract_schema_title)) + + # Extract schema titles + schema_titles = {_extract_schema_title(schema): schema for schema in schemas} + + # Build dependency graph + dependency_graph: Dict[str, Set[str]] = {} + for title, schema in schema_titles.items(): + dependency_graph[title] = _find_schema_dependencies(schema) + + # Perform topological sort with deterministic ordering + sorted_titles: List[str] = [] + visited: Set[str] = set() + temp_visited: Set[str] = set() + + def visit(title: str) -> None: + """Recursive function for topological sort.""" + if title in visited: + return + if title in temp_visited: + # Circular dependency detected, but we'll continue + # For TypedDict, forward references will handle this + return + + temp_visited.add(title) + + # Visit all dependencies first, sorted alphabetically for consistency + dependencies = dependency_graph.get(title, set()) + for dep_title in sorted(dependencies): + if dep_title in schema_titles: # Only consider dependencies we actually have + visit(dep_title) + + temp_visited.remove(title) + visited.add(title) + sorted_titles.append(title) + + # Visit all nodes in alphabetical order for deterministic results + for title in sorted(schema_titles.keys()): + if title not in visited: + visit(title) + + # Return schemas in sorted order + return [schema_titles[title] for title in sorted_titles] + + +def schemas_to_module_def(schemas: List[SchemaPath]) -> ast.Module: + """Converts a list of OpenAPI colocated schemas to a Python module definition (ast.Module).""" + # First, sort schemas to handle dependencies correctly + sorted_schemas = _sort_schemas(schemas) + + # Extract external dependencies needed for imports + external_dependencies = _extract_external_dependencies(schemas) + + # Generate import statements + import_statements = [ + # Standard typing imports + ast.ImportFrom( + module='typing', + names=[ + ast.alias(name='Annotated', asname=None), + ast.alias(name='Optional', asname=None), + ast.alias(name='List', asname=None), + ast.alias(name='Dict', asname=None), + ast.alias(name='Union', asname=None), + ast.alias(name='Literal', asname=None), + ], + level=0, # Absolute import + ), + ast.ImportFrom( + module='typing_extensions', + names=[ast.alias(name='TypedDict', asname=None), ast.alias(name='NotRequired', asname=None)], + level=0, # Absolute import + ), + ] + + # Add imports for external dependencies + for import_path, schema_names in sorted(external_dependencies.items()): + import_statements.append( + ast.ImportFrom( + module=import_path, + names=[ast.alias(name=name, asname=None) for name in sorted(schema_names)], + level=0, # Absolute import + ) + ) + + # Create class definitions for each schema + class_defs = [] + for schema in sorted_schemas: + class_def = schema_to_typed_dict_def(schema) + class_defs.append(class_def) + + # Create module body with imports and classes + module_body = import_statements + class_defs + + return ast.Module(body=module_body, type_ignores=[]) diff --git a/cli/client_gen/schema_packages.py b/cli/client_gen/schema_packages.py new file mode 100644 index 0000000..d46d98d --- /dev/null +++ b/cli/client_gen/schema_packages.py @@ -0,0 +1,48 @@ +import os + +from jsonschema_path.paths import SchemaPath + +from .schema_modules import schemas_to_module_def +from .utils import write_python_file + +"""Utilities for converting an OpenAPI schema collection into a Python schema package.""" + + +def schemas_to_package_directory( + schemas: list[SchemaPath], output_dir: str, depth: int = 0 +) -> None: + """Converts a list of OpenAPI schemas to a Python package directory structure.""" + # We'll start by creating the output directory if it doesn't already exist. + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + # Next, we'll need to seed it with an __init__.py file to make it a package. + # Although, we'll skip this at depth 0, since we'll actually be injecting this into the root package. + if depth > 0: + init_file_path = os.path.join(output_dir, '__init__.py') + with open(init_file_path, 'w') as f: + f.write('# This is an auto-generated schema package. Please do not edit it directly.\n') + + # Now we'll need to sift through the schemas and group them by path prefix. + schema_groups = { + 'modules': {}, + 'packages': {}, + } + for schema in schemas: + module_path = schema.parts[-1].split('.')[depth:] + group_type = 'modules' if len(module_path) == 2 else 'packages' + group_name = module_path[0] + if group_name not in schema_groups[group_type]: + schema_groups[group_type][group_name] = [] + + schema_groups[group_type][group_name].append(schema) + + # Okay, now we can create any module files that need to be created. + for group_name, m_schemas in schema_groups['modules'].items(): + module_def = schemas_to_module_def(m_schemas) + write_python_file(os.path.join(output_dir, f'{group_name}.py'), module_def) + + # And now we can recurse on any sub-packages that need to be created. + for group_name, p_schemas in schema_groups['packages'].items(): + sub_package_dir = os.path.join(output_dir, group_name) + schemas_to_package_directory(p_schemas, sub_package_dir, depth + 1) diff --git a/cli/client_gen/utils.py b/cli/client_gen/utils.py new file mode 100644 index 0000000..1915168 --- /dev/null +++ b/cli/client_gen/utils.py @@ -0,0 +1,39 @@ +import ast +import os +import subprocess + +import rich +import typer +from rich.markdown import Markdown + + +def reformat_python_file(filepath: str) -> None: + """Reformats a Python file using ruff.""" + try: + subprocess.run( + ['uv', 'run', 'ruff', 'format', filepath], check=True, capture_output=True, text=True + ) + subprocess.run( + ['uv', 'run', 'ruff', 'check', '--fix', filepath], check=True, capture_output=True, text=True + ) + except FileNotFoundError: + typer.echo('uv command not found. Please ensure uv is installed and in your PATH.', err=True) + raise typer.Exit(1) + except subprocess.CalledProcessError as e: + typer.echo(f'Error formatting {filepath} with uv ruff format: {e}', err=True) + + +def write_python_file(filepath: str, module_node: ast.Module) -> None: + """Writes an AST module to a Python file, and reformats it appropriately with ruff.""" + + # Ensure the output directory exists + output_dir = os.path.dirname(filepath) + if output_dir: # Check if output_dir is not an empty string (i.e., file is in current dir) + os.makedirs(output_dir, exist_ok=True) + + with open(filepath, 'w') as f: + f.write(ast.unparse(ast.fix_missing_locations(module_node))) + + reformat_python_file(filepath) + + rich.print(Markdown(f'Generated `{filepath}`.')) diff --git a/cli/main.py b/cli/main.py new file mode 100644 index 0000000..e54816e --- /dev/null +++ b/cli/main.py @@ -0,0 +1,69 @@ +import os +import re +from typing import Annotated + +import typer +from openapi_core import OpenAPI + +from cli.client_gen.module_mapping import update_module_mapping +from cli.client_gen.resource_packages import resources_to_package_directory +from cli.client_gen.schema_packages import schemas_to_package_directory + +REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) +SPEC_FILE = os.path.join(REPO_ROOT, 'dialpad_api_spec.json') +CLIENT_DIR = os.path.join(REPO_ROOT, 'src', 'dialpad') + +app = typer.Typer() + + +@app.command('preprocess-spec') +def reformat_spec(): + """Applies some preprocessing to the OpenAPI spec.""" + # This is extremely hackish, but gets the job done for now... + with open(SPEC_FILE, 'r') as f: + spec_file_contents = f.read() + + replace_ops = [ + (r'protos(\.\w+\.\w+)', r'schemas\1'), + (r'frontend\.schemas(\.\w+\.\w+)', r'schemas\1'), + ] + + for pattern, replacement in replace_ops: + spec_file_contents = re.sub(pattern, replacement, spec_file_contents) + + # Write the modified contents back to the file + with open(SPEC_FILE, 'w') as f: + f.write(spec_file_contents) + + typer.echo(f"Reformatted OpenAPI spec at '{SPEC_FILE}'") + + +@app.command('update-resource-module-mapping') +def update_resource_module_mapping( + interactive: Annotated[ + bool, typer.Option(help='Update resource module mapping interactively') + ] = False, +): + """Updates the resource module mapping with any new paths and operations found in the OpenAPI spec.""" + + open_api_spec = OpenAPI.from_file_path(SPEC_FILE) + update_module_mapping(open_api_spec.spec, interactive=interactive) + + +@app.command('generate-client') +def generate_client(): + """Regenerates all the client components from the OpenAPI spec.""" + open_api_spec = OpenAPI.from_file_path(SPEC_FILE) + + # Gather all the schema components from the OpenAPI spec + all_schemas = [v for _k, v in (open_api_spec.spec / 'components' / 'schemas').items()] + + # Write the generated schema package to the client directory + schemas_to_package_directory(all_schemas, CLIENT_DIR) + + # Write the generated resource modules to the client directory + resources_to_package_directory(open_api_spec.spec, os.path.join(CLIENT_DIR, 'resources')) + + +if __name__ == '__main__': + app() diff --git a/dev_requirements.txt b/dev_requirements.txt deleted file mode 100644 index a5fbdbf..0000000 --- a/dev_requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -coverage == 5.5 -pytest -pipenv == 2021.5.29 -pytest-sugar == 0.9.4 -pyrsistent == 0.16.1 -pytest-cov -bump2version -swagger-spec-validator == 2.7.3 -swagger-stub == 0.2.1 -jsonschema<4.0 -wheel -cython<3.0.0 -pyyaml==5.4.1 -py -git+https://github.com/jakedialpad/swagger-parser@v1.0.1b#egg=swagger-parser diff --git a/dialpad/client.py b/dialpad/client.py deleted file mode 100644 index 3736a70..0000000 --- a/dialpad/client.py +++ /dev/null @@ -1,183 +0,0 @@ - -import requests - -from cached_property import cached_property - -from .resources import ( - AppSettingsResource, - SMSResource, - RoomResource, - UserResource, - CallResource, - NumberResource, - OfficeResource, - WebhookResource, - CompanyResource, - ContactResource, - CallbackResource, - CallCenterResource, - CallRouterResource, - DepartmentResource, - TranscriptResource, - UserDeviceResource, - StatsExportResource, - SubscriptionResource, - BlockedNumberResource, - EventSubscriptionResource -) - - -hosts = dict( - live='https://dialpad.com', - sandbox='https://sandbox.dialpad.com' -) - - -class DialpadClient(object): - def __init__(self, token, sandbox=False, base_url=None, company_id=None): - self._token = token - self._session = requests.Session() - self._base_url = base_url or hosts.get('sandbox' if sandbox else 'live') - self._company_id = company_id - - @property - def company_id(self): - return self._company_id - - @company_id.setter - def company_id(self, value): - self._company_id = value - - @company_id.deleter - def company_id(self): - del self._company_id - - def _url(self, *path): - path = ['%s' % p for p in path] - return '/'.join([self._base_url, 'api', 'v2'] + path) - - def _cursor_iterator(self, response_json, path, method, data, headers): - for i in response_json['items']: - yield i - - data = dict(data or {}) - - while 'cursor' in response_json: - data['cursor'] = response_json['cursor'] - response = self._raw_request(path, method, data, headers) - response.raise_for_status() - response_json = response.json() - for i in response_json.get('items', []): - yield i - - def _raw_request(self, path, method='GET', data=None, headers=None): - url = self._url(*path) - headers = headers or dict() - if self.company_id: - headers.update({'DP-Company-ID': str(self.company_id)}) - - headers.update({'Authorization': 'Bearer %s' % self._token}) - if str(method).upper() in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']: - return getattr(self._session, str(method).lower())( - url, - headers=headers, - json=data if method != 'GET' else None, - params=data if method == 'GET' else None, - ) - raise ValueError('Unsupported method "%s"' % method) - - def request(self, path, method='GET', data=None, headers=None): - response = self._raw_request(path, method, data, headers) - response.raise_for_status() - - if response.status_code == 204: # No Content - return None - - response_json = response.json() - response_keys = set(k for k in response_json) - # If the response contains the 'items' key, (and maybe 'cursor'), then this is a cursorized - # list response. - if 'items' in response_keys and not response_keys - {'cursor', 'items'}: - return self._cursor_iterator( - response_json, path=path, method=method, data=data, headers=headers) - return response_json - - @cached_property - def app_settings(self): - return AppSettingsResource(self) - - @cached_property - def blocked_number(self): - return BlockedNumberResource(self) - - @cached_property - def call(self): - return CallResource(self) - - @cached_property - def call_router(self): - return CallRouterResource(self) - - @cached_property - def callback(self): - return CallbackResource(self) - - @cached_property - def callcenter(self): - return CallCenterResource(self) - - @cached_property - def company(self): - return CompanyResource(self) - - @cached_property - def contact(self): - return ContactResource(self) - - @cached_property - def department(self): - return DepartmentResource(self) - - @cached_property - def event_subscription(self): - return EventSubscriptionResource(self) - - @cached_property - def number(self): - return NumberResource(self) - - @cached_property - def office(self): - return OfficeResource(self) - - @cached_property - def room(self): - return RoomResource(self) - - @cached_property - def sms(self): - return SMSResource(self) - - @cached_property - def stats(self): - return StatsExportResource(self) - - @cached_property - def subscription(self): - return SubscriptionResource(self) - - @cached_property - def transcript(self): - return TranscriptResource(self) - - @cached_property - def user(self): - return UserResource(self) - - @cached_property - def userdevice(self): - return UserDeviceResource(self) - - @cached_property - def webhook(self): - return WebhookResource(self) diff --git a/dialpad/resources/__init__.py b/dialpad/resources/__init__.py deleted file mode 100644 index 3d0693e..0000000 --- a/dialpad/resources/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -from .app_settings import AppSettingsResource -from .blocked_number import BlockedNumberResource -from .call import CallResource -from .call_router import CallRouterResource -from .callback import CallbackResource -from .callcenter import CallCenterResource -from .company import CompanyResource -from .contact import ContactResource -from .department import DepartmentResource -from .event_subscription import EventSubscriptionResource -from .number import NumberResource -from .office import OfficeResource -from .room import RoomResource -from .sms import SMSResource -from .stats import StatsExportResource -from .subscription import SubscriptionResource -from .transcript import TranscriptResource -from .user import UserResource -from .userdevice import UserDeviceResource -from .webhook import WebhookResource diff --git a/dialpad/resources/app_settings.py b/dialpad/resources/app_settings.py deleted file mode 100644 index 1f74d05..0000000 --- a/dialpad/resources/app_settings.py +++ /dev/null @@ -1,25 +0,0 @@ -from .resource import DialpadResource - -class AppSettingsResource(DialpadResource): - """AppSettingsResource implements python bindings for the Dialpad API's app-settings - endpoints. - - See https://developers.dialpad.com/reference/appsettingsapi_getappsettings for additional - documentation. - """ - _resource_path = ['app', 'settings'] - - def get(self, target_id=None, target_type=None): - """Gets the app settings of the oauth app that is associated with the API key. - - If a target is specified, it will fetch the settings for that target, - otherwise it will fetch the company-level settings. - - Args: - target_id(int, optional): The target's id. - target_type(str, optional): The target's type. - - See Also: - https://developers.dialpad.com/reference/appsettingsapi_getappsettings - """ - return self.request(method='GET', data=dict(target_id=target_id, target_type=target_type)) diff --git a/dialpad/resources/blocked_number.py b/dialpad/resources/blocked_number.py deleted file mode 100644 index 0339d74..0000000 --- a/dialpad/resources/blocked_number.py +++ /dev/null @@ -1,57 +0,0 @@ -from .resource import DialpadResource - -class BlockedNumberResource(DialpadResource): - """BlockedNumberResource implements python bindings for the Dialpad API's blocked-number - endpoints. - - See https://developers.dialpad.com/reference#blockednumbers for additional documentation. - """ - _resource_path = ['blockednumbers'] - - def list(self, limit=25, **kwargs): - """List all numbers that have been flagged as "blocked" via the API. - - Args: - limit (int, optional): The number of numbers to fetch per request. - - See Also: - https://developers.dialpad.com/reference#blockednumberapi_listnumbers - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def block_numbers(self, numbers): - """Blocks inbound calls from the specified numbers. - - Args: - numbers (list, required): A list of e164-formatted numbers to block. - - See Also: - https://developers.dialpad.com/reference#blockednumberapi_addnumbers - """ - return self.request(['add'], method='POST', data={'numbers': numbers}) - - def unblock_numbers(self, numbers): - """Unblocks inbound calls from the specified numbers. - - Args: - numbers (list, required): A list of e164-formatted numbers to unblock. - - See Also: - https://developers.dialpad.com/reference#blockednumberapi_removenumbers - """ - return self.request(['remove'], method='POST', data={'numbers': numbers}) - - def get(self, number): - """Gets a number object, provided it has been blocked by the API. - - Note: - This API call will 404 if the number is not blocked, and return {"number": } if the - number is blocked. - - Args: - number (str, required): An e164-formatted number. - - See Also: - https://developers.dialpad.com/reference#blockednumberapi_getnumber - """ - return self.request([number], method='GET') diff --git a/dialpad/resources/call.py b/dialpad/resources/call.py deleted file mode 100644 index 8abe141..0000000 --- a/dialpad/resources/call.py +++ /dev/null @@ -1,54 +0,0 @@ -from .resource import DialpadResource - -class CallResource(DialpadResource): - """CallResource implements python bindings for the Dialpad API's call endpoints. - - See https://developers.dialpad.com/reference#call for additional documentation. - """ - _resource_path = ['call'] - - def initiate_call(self, phone_number, user_id, **kwargs): - """Initiates an oubound call to the specified phone number on behalf of the specified user. - - Note: - This API will initiate the call by ringing the user's devices as well as ringing the specified - number. When the user answers on their device, they will be connected with the call that is - ringing the specified number. - - Optionally, group_type and group_id can be specified to cause the call to be routed through - the specified group. This would be equivelant to the User initiating the call by selecting the - specified group in the "New Call As" dropdown in the native app, or calling a contact that - belongs to that group via the native app. - - In particular, the call will show up in that group's section of the app, and the external - party will receive a call from the primary number of the specified group. - - Additionally, a specific device_id can be specified to cause that specific user-device to - ring, rather than all of the user's devices. - - Args: - phone_number (str, required): The e164-formatted number that should be called. - user_id (int, required): The ID of the user that should be taking the call. - group_id (int, optional): The ID of the call center, department, or office that should be used - to initiate the call. - group_type (str, optional): One of "office", "department", or "callcenter", corresponding to - the type of ID passed into group_type. - device_id (str, optional): The ID of the specific user device that should ring. - custom_data (str, optional): Free-form extra data to associate with the call. - - See Also: - https://developers.dialpad.com/reference#callapi_call - """ - return self.request(method='POST', data=dict(phone_number=phone_number, user_id=user_id, - **kwargs)) - - def get_info(self, call_id): - """Gets call status and other information. - - Args: - call_id (int, required): The ID of the call. - - See Also: - https://developers.dialpad.com/reference/callapi_getcallinfo - """ - return self.request([call_id], method='GET') diff --git a/dialpad/resources/call_router.py b/dialpad/resources/call_router.py deleted file mode 100644 index ebb65e1..0000000 --- a/dialpad/resources/call_router.py +++ /dev/null @@ -1,108 +0,0 @@ -from .resource import DialpadResource - -class CallRouterResource(DialpadResource): - """CallRouterResource implements python bindings for the Dialpad API's call router endpoints. - - See https://developers.dialpad.com/reference#callrouters for additional documentation. - """ - _resource_path = ['callrouters'] - - def list(self, office_id, **kwargs): - """Initiates an oubound call to the specified phone number on behalf of the specified user. - - Args: - office_id (int, required): The ID of the office to which the routers belong. - limit (int, optional): The number of routers to fetch per request. - - See Also: - https://developers.dialpad.com/reference#callrouterapi_listcallrouters - """ - return self.request(method='GET', data=dict(office_id=office_id, **kwargs)) - - def create(self, name, default_target_id, default_target_type, office_id, routing_url, **kwargs): - """Creates a new API-based call router. - - Args: - name (str, required): human-readable display name for the router. - default_target_id (int, required): The ID of the target that should be used as a fallback - destination for calls if the call router is disabled. - default_target_type (str, required): The entity type of the default target. - office_id (int, required): The ID of the office to which the router should belong. - routing_url (str, required): The URL that should be used to drive call routing decisions. - secret (str, optional): The call router's signature secret. This is a plain text string that - you should generate with a minimum length of 32 characters. - enabled (bool, optional): If set to False, the call router will skip the routing url and - instead forward calls straight to the default target. - - See Also: - https://developers.dialpad.com/reference#callrouterapi_createcallrouter - """ - return self.request(method='POST', data=dict( - name=name, - default_target_id=default_target_id, - default_target_type=default_target_type, - office_id=office_id, - routing_url=routing_url, - **kwargs) - ) - - def delete(self, router_id): - """Deletes the API call router with the given ID. - - Args: - router_id (str, required): The ID of the router to delete. - - See Also: - https://developers.dialpad.com/reference#callrouterapi_deletecallrouter - """ - return self.request([router_id], method='DELETE') - - def get(self, router_id): - """Fetches the API call router with the given ID. - - Args: - router_id (str, required): The ID of the router to fetch. - - See Also: - https://developers.dialpad.com/reference#callrouterapi_getcallrouter - """ - return self.request([router_id], method='GET') - - def patch(self, router_id, **kwargs): - """Updates the API call router with the given ID. - - Args: - router_id (str, required): The ID of the router to update. - name (str, required): human-readable display name for the router. - default_target_id (int, required): The ID of the target that should be used as a fallback - destination for calls if the call router is disabled. - default_target_type (str, required): The entity type of the default target. - office_id (int, required): The ID of the office to which the router should belong. - routing_url (str, required): The URL that should be used to drive call routing decisions. - secret (str, optional): The call router's signature secret. This is a plain text string that - you should generate with a minimum length of 32 characters. - enabled (bool, optional): If set to False, the call router will skip the routing url and - instead forward calls straight to the default target - reset_error_count (bool, optional): Sets the auto-disablement routing error count back to - zero. (See API docs for more details) - - See Also: - https://developers.dialpad.com/reference#callrouterapi_updatecallrouter - """ - return self.request([router_id], method='PATCH', data=kwargs) - - def assign_number(self, router_id, **kwargs): - """Assigns a number to the call router. - - Args: - router_id (str, required): The ID of the router to assign the number. - area_code (str, optional): An area code to attempt to use if a reserved pool number is not - provided. If no area code is provided, the office's area code will - be used. - number (str, optional): A phone number from the reserved pool to attempt to assign. - - See Also: - https://developers.dialpad.com/reference#numberapi_assignnumbertocallrouter - """ - return self.request([router_id, 'assign_number'], method='POST', data=kwargs) - diff --git a/dialpad/resources/callback.py b/dialpad/resources/callback.py deleted file mode 100644 index 367edfa..0000000 --- a/dialpad/resources/callback.py +++ /dev/null @@ -1,49 +0,0 @@ -from .resource import DialpadResource - -class CallbackResource(DialpadResource): - """CallbackResource implements python bindings for the Dialpad API's callback endpoints. - - See https://developers.dialpad.com/reference#callback for additional documentation. - """ - _resource_path = ['callback'] - - def enqueue_callback(self, call_center_id, phone_number): - """Requests a call-back for the specified number by adding it to the callback queue for the - specified call center. - - The call back is added to the queue for the call center like a regular call, and a call is - initiated when the next operator becomes available. This API respects all existing call center - settings, e.g. business / holiday hours and queue settings. This API currently does not allow - international call backs. Duplicate call backs for a given number and call center are not - allowed. - - Args: - call_center_id (str, required): The ID of the call center for which the callback should be - enqueued. - phone_number (str, required): The e164-formatted number that should be added to the callback - queue. - - See Also: - https://developers.dialpad.com/reference#callapi_callback - """ - return self.request(method='POST', data=dict(call_center_id=call_center_id, - phone_number=phone_number)) - - def validate_callback(self, call_center_id, phone_number): - """Performs a dry-run of creating a callback request, but does not add it to the call center - queue. - - This performs the same validation logic as when actually enqueuing a callback request, allowing - early identification of problems which would prevent a successful callback request. - - Args: - call_center_id (str, required): The ID of the call center for which the callback would be - enqueued. - phone_number (str, required): The e164-formatted number that would be added to the callback - queue. - - See Also: - https://developers.dialpad.com/reference/callapi_validatecallback - """ - return self.request(['validate'], method='POST', data=dict(call_center_id=call_center_id, - phone_number=phone_number)) diff --git a/dialpad/resources/callcenter.py b/dialpad/resources/callcenter.py deleted file mode 100644 index 0572293..0000000 --- a/dialpad/resources/callcenter.py +++ /dev/null @@ -1,68 +0,0 @@ -from .resource import DialpadResource - -class CallCenterResource(DialpadResource): - """CallCenterResource implements python bindings for the Dialpad API's call center endpoints. - See https://developers.dialpad.com/reference#callcenters for additional documentation. - """ - - _resource_path = ['callcenters'] - - def get(self, call_center_id): - """Gets a call center by ID. - - Args: - call_center_id (int, required): The ID of the call center to retrieve. - - See Also: - https://developers.dialpad.com/reference#callcenterapi_getcallcenter - """ - return self.request([call_center_id], method='GET') - - def get_operators(self, call_center_id): - """Gets the list of users who are operators for the specified call center. - - Args: - call_center_id (int, required): The ID of the call center. - - See Also: - https://developers.dialpad.com/reference#callcenterapi_listoperators - """ - return self.request([call_center_id, 'operators'], method='GET') - - def add_operator(self, call_center_id, user_id, **kwargs): - """Adds the specified user as an operator of the specified call center. - - Args: - call_center_id (int, required): The ID of the call center. - user_id (int, required): The ID of the user to add as an operator. - skill_level (int, optional): Skill level of the operator. Integer value in range 1 - 100. - Default 100 - role (str, optional): The role of the new operator ('operator', 'supervisor', or 'admin'). - Default 'operator' - license_type (str, optional): The type of license to assign to the new operator if a license - is required ('agents', or 'lite_support_agents'). - Default 'agents' - keep_paid_numbers (bool, optional): If the operator is currently on a license that provides - paid numbers and `license_type` is set to - `lite_support_agents`, this option will determine if the - operator keeps those numbers. Set to False for the - numbers to be removed. - Default True - - See Also: - https://developers.dialpad.com/reference#callcenterapi_addoperator - """ - kwargs['user_id'] = user_id - return self.request([call_center_id, 'operators'], method='POST', data=kwargs) - - def remove_operator(self, call_center_id, user_id): - """Removes the specified user from the specified call center. - - Args: - call_center_id (int, required): The ID of the call center. - user_id (int, required): The ID of the user to remove. - - See Also: - https://developers.dialpad.com/reference#callcenterapi_removeoperator - """ - return self.request([call_center_id, 'operators'], method='DELETE', data={'user_id': user_id}) diff --git a/dialpad/resources/company.py b/dialpad/resources/company.py deleted file mode 100644 index 70c06d7..0000000 --- a/dialpad/resources/company.py +++ /dev/null @@ -1,16 +0,0 @@ -from .resource import DialpadResource - -class CompanyResource(DialpadResource): - """CompanyResource implements python bindings for the Dialpad API's company endpoints. - - See https://developers.dialpad.com/reference#company for additional documentation. - """ - _resource_path = ['company'] - - def get(self): - """Gets the company resource. - - See Also: - https://developers.dialpad.com/reference#companyapi_getcompany - """ - return self.request(method='GET') diff --git a/dialpad/resources/contact.py b/dialpad/resources/contact.py deleted file mode 100644 index 197ba0e..0000000 --- a/dialpad/resources/contact.py +++ /dev/null @@ -1,105 +0,0 @@ -from .resource import DialpadResource - -class ContactResource(DialpadResource): - """ContactResource implements python bindings for the Dialpad API's contact endpoints. - - See https://developers.dialpad.com/reference#contacts for additional documentation. - """ - _resource_path = ['contacts'] - - def list(self, limit=25, **kwargs): - """Lists contacts in the company. - - Args: - limit (int, optional): The number of contacts to fetch per request. - owner_id (int, optional): A specific user who's contacts should be listed. - - See Also: - https://developers.dialpad.com/reference#contactapi_listcontacts - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def create(self, first_name, last_name, **kwargs): - """Creates a new contact. - - Args: - first_name (str, required): The contact's first name. - last_name (str, required): The contact's family name. - company_name (str, optional): The name of the contact's company. - emails (list, optional): A list of email addresses associated with the contact. - extension (str, optional): The contact's extension number. - job_title (str, optional): The contact's job title. - owner_id (str, optional): The ID of the user who should own this contact. If no owner_id is - specified, then a company-level shared contact will be created. - phones (list, optional): A list of e164 numbers that belong to this contact. - trunk_group (str, optional): The contact's trunk group. - urls (list, optional): A list of urls that pertain to this contact. - - See Also: - https://developers.dialpad.com/reference#contactapi_createcontact - """ - return self.request(method='POST', data=dict(first_name=first_name, last_name=last_name, - **kwargs)) - - def create_with_uid(self, first_name, last_name, uid, **kwargs): - """Creates a new contact with a prescribed unique identifier. - - Args: - first_name (str, required): The contact's first name. - last_name (str, required): The contact's family name. - uid (str, required): A unique identifier that should be included in the contact's ID. - company_name (str, optional): The name of the contact's company. - emails (list, optional): A list of email addresses associated with the contact. - extension (str, optional): The contact's extension number. - job_title (str, optional): The contact's job title. - phones (list, optional): A list of e164 numbers that belong to this contact. - trunk_group (str, optional): The contact's trunk group. - urls (list, optional): A list of urls that pertain to this contact. - - See Also: - https://developers.dialpad.com/reference#contactapi_createcontactwithuid - """ - return self.request(method='PUT', data=dict(first_name=first_name, last_name=last_name, uid=uid, - **kwargs)) - - def delete(self, contact_id): - """Deletes the specified contact. - - Args: - contact_id (str, required): The ID of the contact to delete. - - See Also: - https://developers.dialpad.com/reference#contactapi_deletecontact - """ - return self.request([contact_id], method='DELETE') - - def get(self, contact_id): - """Gets a contact by ID. - - Args: - contact_id (str, required): The ID of the contact. - - See Also: - https://developers.dialpad.com/reference#contactapi_getcontact - """ - return self.request([contact_id], method='GET') - - def patch(self, contact_id, **kwargs): - """Updates an existing contact. - - Args: - contact_id (str, required): The ID of the contact. - first_name (str, optional): The contact's first name. - last_name (str, optional): The contact's family name. - company_name (str, optional): The name of the contact's company. - emails (list, optional): A list of email addresses associated with the contact. - extension (str, optional): The contact's extension number. - job_title (str, optional): The contact's job title. - phones (list, optional): A list of e164 numbers that belong to this contact. - trunk_group (str, optional): The contact's trunk group. - urls (list, optional): A list of urls that pertain to this contact. - - See Also: - https://developers.dialpad.com/reference#contactapi_updatecontact - """ - return self.request([contact_id], method='PATCH', data=kwargs) diff --git a/dialpad/resources/department.py b/dialpad/resources/department.py deleted file mode 100644 index 0ee9cfc..0000000 --- a/dialpad/resources/department.py +++ /dev/null @@ -1,65 +0,0 @@ -from .resource import DialpadResource - -class DepartmentResource(DialpadResource): - """DepartmentResource implements python bindings for the Dialpad API's department endpoints. - See https://developers.dialpad.com/reference#departments for additional documentation. - """ - - _resource_path = ['departments'] - - def get(self, department_id): - """Gets a department by ID. - - Args: - department_id (int, required): The ID of the department to retrieve. - - See Also: - https://developers.dialpad.com/reference#departmentapi_getdepartment - """ - return self.request([department_id], method='GET') - - def get_operators(self, department_id): - """Gets the list of users who are operators for the specified department. - - Args: - department_id (int, required): The ID of the department. - - See Also: - https://developers.dialpad.com/reference#departmentapi_listoperators - """ - return self.request([department_id, 'operators'], method='GET') - - def add_operator(self, department_id, operator_id, operator_type, role='operator'): - """Adds the specified user as an operator of the specified department. - - Args: - department_id (int, required): The ID of the department. - operator_id (int, required): The ID of the operator to add. - operator_type (str, required): Type of the operator to add ('user' or 'room'). - role (str, optional): The role of the new operator ('operator' or 'admin'). - Default 'operator' - - See Also: - https://developers.dialpad.com/reference#departmentapi_addoperator - """ - return self.request([department_id, 'operators'], method='POST', data={ - 'operator_id': operator_id, - 'operator_type': operator_type, - 'role': role, - }) - - def remove_operator(self, department_id, operator_id, operator_type): - """Removes the specified user from the specified department. - - Args: - department_id (int, required): The ID of the department. - operator_id (int, required): The ID of the operator to remove. - operator_type (str, required): Type of the operator to remove ('user' or 'room'). - - See Also: - https://developers.dialpad.com/reference#departmentapi_removeoperator - """ - return self.request([department_id, 'operators'], method='DELETE', data={ - 'operator_id': operator_id, - 'operator_type': operator_type, - }) diff --git a/dialpad/resources/event_subscription.py b/dialpad/resources/event_subscription.py deleted file mode 100644 index 4b83f49..0000000 --- a/dialpad/resources/event_subscription.py +++ /dev/null @@ -1,145 +0,0 @@ -from .resource import DialpadResource - -class EventSubscriptionResource(DialpadResource): - """EventSubscriptionResource implements python bindings for the Dialpad API's event subscription - endpoints. - - See https://developers.dialpad.com/reference#event for additional documentation. - """ - _resource_path = ['event-subscriptions'] - - def list_call_event_subscriptions(self, limit=25, **kwargs): - """Lists call event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#calleventsubscriptionapi_listcalleventsubscriptions - """ - return self.request(['call'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_call_event_subscription(self, subscription_id): - """Gets a specific call event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#calleventsubscriptionapi_getcalleventsubscription - """ - return self.request(['call', subscription_id], method='GET') - - def put_call_event_subscription(self, subscription_id, url, enabled=True, group_calls_only=False, - **kwargs): - """Update or create a call event subscription. - - The subscription_id is required. If the ID exists, then this call will update the subscription - resource. If the ID does not exist, then it will create a new subscription with that ID. - - Args: - subscription_id (str, required): The ID of the subscription - url (str, required): The URL which should be called when the subscription fires - secret (str, optional): A secret to use to encrypt subscription event payloads - enabled (bool, optional): Whether or not the subscription should actually fire - group_calls_only (bool, optional): Whether to limit the subscription to only fire if the call - is a group call - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - call_states (list, optional): The specific types of call events that should trigger the - subscription (any of "preanswer", "calling", "ringing", - "connected", "merged", "hold", "queued", "voicemail", - "eavesdrop", "monitor", "barge", "hangup", "blocked", - "admin", "parked", "takeover", "all", "postcall", - "transcription", or "recording") - - See Also: - https://developers.dialpad.com/reference#calleventsubscriptionapi_createorupdatecalleventsubscription - """ - - return self.request(['call', subscription_id], method='PUT', - data=dict(url=url, enabled=enabled, group_calls_only=group_calls_only, - **kwargs)) - - def delete_call_event_subscription(self, subscription_id): - """Deletes a specific call event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#calleventsubscriptionapi_deletecalleventsubscription - """ - return self.request(['call', subscription_id], method='DELETE') - - - def list_sms_event_subscriptions(self, limit=25, **kwargs): - """Lists sms event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_listsmseventsubscriptions - """ - return self.request(['sms'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_sms_event_subscription(self, subscription_id): - """Gets a specific sms event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_getsmseventsubscription - """ - return self.request(['sms', subscription_id], method='GET') - - def put_sms_event_subscription(self, subscription_id, url, direction, enabled=True, - **kwargs): - """Update or create an sms event subscription. - - The subscription_id is required. If the ID exists, then this call will update the subscription - resource. If the ID does not exist, then it will create a new subscription with that ID. - - Args: - subscription_id (str, required): The ID of the subscription - url (str, required): The URL which should be called when the subscription fires - direction (str, required): The SMS direction that should fire the subscripion ("inbound", - "outbound", or "all") - enabled (bool, optional): Whether or not the subscription should actually fire - secret (str, optional): A secret to use to encrypt subscription event payloads - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_createorupdatesmseventsubscription - """ - - return self.request(['sms', subscription_id], method='PUT', - data=dict(url=url, enabled=enabled, direction=direction, - **kwargs)) - - def delete_sms_event_subscription(self, subscription_id): - """Deletes a specific sms event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_deletesmseventsubscription - """ - return self.request(['sms', subscription_id], method='DELETE') - diff --git a/dialpad/resources/number.py b/dialpad/resources/number.py deleted file mode 100644 index 695cbff..0000000 --- a/dialpad/resources/number.py +++ /dev/null @@ -1,86 +0,0 @@ -from .resource import DialpadResource - -class NumberResource(DialpadResource): - """NumberResource implements python bindings for the Dialpad API's number endpoints. - - See https://developers.dialpad.com/reference#numbers for additional documentation. - """ - _resource_path = ['numbers'] - - def list(self, limit=25, **kwargs): - """List all phone numbers in the company. - - Args: - limit (int, optional): The number of numbers to fetch per request - status (str, optional): If provided, the results will only contain numbers with the specified - status. Must be one of: "available", "pending", "office", - "department", "call_center", "user", "room", "porting", "call_router", - or "dynamic_caller" - - See Also: - https://developers.dialpad.com/reference#numberapi_listnumbers - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def get(self, number): - """Gets a specific number object. - - Args: - number (str, required): An e164-formatted number. - - See Also: - https://developers.dialpad.com/reference#numberapi_getnumber - """ - return self.request([number], method='GET') - - def unassign(self, number, release=False): - """Unassigns the specified number. - - Args: - number (str, required): An e164-formatted number. - release (bool, optional): If the "release" flag is omitted or set to False, the number will - be returned to the company pool (i.e. your company will still own - the number, but it will no longer be assigned to any targets). - If the "release" flag is set, then the number will be beamed back - to the Dialpad mothership. - - See Also: - https://developers.dialpad.com/reference#numberapi_unassignnumber - """ - return self.request([number], method='DELETE', data={'release': release}) - - def assign(self, number, target_id, target_type, primary=True): - """Assigns the specified number to the specified target. - - Args: - number (str, required): The e164-formatted number that should be assigned. - target_id (int, required): The ID of the target to which the number should be assigned. - target_type (str, required): The type corresponding to the provided target ID. - primary (bool, optional): (Defaults to True) If the "primary" flag is set, then the assigned - number will become the primary number of the specified target. - - See Also: - https://developers.dialpad.com/reference#numberapi_assignnumber - """ - return self.request(['assign'], method='POST', data={ - 'number': number, - 'target_id': target_id, - 'target_type': target_type, - 'primary': primary - }) - - def format(self, number, country_code=None): - """Converts local number to E.164 or E.164 to local format. - - Args: - number (str, required): The phone number in local or E.164 format. - country_code (str, optional): Country code in ISO 3166-1 alpha-2 format such as "US". - Required when sending a local formatted phone number. - - See Also: - https://developers.dialpad.com/reference#formatapi_formatnumber - """ - return self.request(['format'], method='POST', data={ - 'number': number, - 'country_code': country_code - }) diff --git a/dialpad/resources/office.py b/dialpad/resources/office.py deleted file mode 100644 index 0bbfe1d..0000000 --- a/dialpad/resources/office.py +++ /dev/null @@ -1,105 +0,0 @@ -from .resource import DialpadResource - -class OfficeResource(DialpadResource): - """OfficeResource implements python bindings for the Dialpad API's office endpoints. - See https://developers.dialpad.com/reference#offices for additional documentation. - """ - - _resource_path = ['offices'] - - def list(self, limit=25, **kwargs): - """Lists the company's offices. - - Args: - limit (int, optional): the number of offices to fetch per request. - - See Also: - https://developers.dialpad.com/reference#officeapi_listoffices - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def get(self, office_id): - """Gets an office by ID. - - Args: - office_id (int, required): The ID of the office to retrieve. - - See Also: - https://developers.dialpad.com/reference#officeapi_getoffice - """ - return self.request([office_id], method='GET') - - def assign_number(self, office_id, **kwargs): - """Assigns a phone number to the specified office - - Args: - office_id (int, required): The ID of the office to which the number should be assigned. - number (str, optional): An e164 number that has already been allocated to the company's - reserved number pool that should be re-assigned to this office. - area_code (str, optional): The area code to use to filter the set of available numbers to be - assigned to this office. - - See Also: - https://developers.dialpad.com/reference#numberapi_assignnumbertooffice - """ - return self.request([office_id, 'assign_number'], method='POST', data=kwargs) - - def get_operators(self, office_id): - """Gets the list of users who are operators for the specified office. - - Args: - office_id (int, required): The ID of the office. - - See Also: - https://developers.dialpad.com/reference#officeapi_listoperators - """ - return self.request([office_id, 'operators'], method='GET') - - def unassign_number(self, office_id, number): - """Unassigns the specified number from the specified office. - - Args: - office_id (int, required): The ID of the office. - number (str, required): The e164-formatted number that should be unassigned from the office. - - See Also: - https://developers.dialpad.com/reference#numberapi_unassignnumberfromoffice - """ - return self.request([office_id, 'unassign_number'], method='POST', data={'number': number}) - - def get_call_centers(self, office_id, limit=25, **kwargs): - """Lists the call centers under the specified office. - - Args: - office_id (int, required): The ID of the office. - limit (int, optional): the number of call centers to fetch per request. - - See Also: - https://developers.dialpad.com/reference#callcenterapi_listcallcenters - """ - return self.request([office_id, 'callcenters'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_departments(self, office_id, limit=25, **kwargs): - """Lists the departments under the specified office. - - Args: - office_id (int, required): The ID of the office. - limit (int, optional): the number of departments to fetch per request. - - See Also: - https://developers.dialpad.com/reference#departmentapi_listdepartments - """ - return self.request([office_id, 'departments'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_plan(self, office_id): - """Gets the plan associated with the office. - - (i.e. a breakdown of the licenses that have been purchased for the specified office) - - Args: - office_id (int, required): The ID of the office. - - See Also: - https://developers.dialpad.com/reference#planapi_getplan - """ - return self.request([office_id, 'plan'], method='GET') diff --git a/dialpad/resources/resource.py b/dialpad/resources/resource.py deleted file mode 100644 index 811cfdf..0000000 --- a/dialpad/resources/resource.py +++ /dev/null @@ -1,11 +0,0 @@ -class DialpadResource(object): - _resource_path = None - - def __init__(self, client, basepath=None): - self._client = client - - def request(self, path=None, *args, **kwargs): - if self._resource_path is None: - raise NotImplementedError('DialpadResource subclasses must have a _resource_path property') - path = path or [] - return self._client.request(self._resource_path + path, *args, **kwargs) diff --git a/dialpad/resources/room.py b/dialpad/resources/room.py deleted file mode 100644 index 81e9ed3..0000000 --- a/dialpad/resources/room.py +++ /dev/null @@ -1,163 +0,0 @@ -from .resource import DialpadResource - -class RoomResource(DialpadResource): - """RoomResource implements python bindings for the Dialpad API's room endpoints. - See https://developers.dialpad.com/reference#rooms for additional documentation. - """ - - _resource_path = ['rooms'] - - def list(self, limit=25, **kwargs): - """Lists rooms in the company. - - Args: - limit (int, optional): The number of rooms to fetch per request. - office_id (int, optional): If specified, only rooms associated with that office will be - returned. - - See Also: - https://developers.dialpad.com/reference#roomapi_listrooms - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def create(self, name, office_id): - """Creates a new room with the specified name within the specified office. - - Args: - name (str, required): A human-readable name for the room. - office_id (int, required): The ID of the office. - - See Also: - https://developers.dialpad.com/reference#roomapi_createroom - """ - return self.request(method='POST', data={'name': name, 'office_id': office_id}) - - def generate_international_pin(self, customer_ref): - """Creates a PIN to allow an international call to be made from a room phone. - - Args: - customer_ref (str, required): An identifier to be printed in the usage summary. Typically used - for identifying the person who requested the PIN - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_createinternationalpin - """ - return self.request(['international_pin'], method='POST', data={'customer_ref': customer_ref}) - - def delete(self, room_id): - """Deletes a room by ID. - - Args: - room_id (str, required): The ID of the room to be deleted. - - See Also: - https://developers.dialpad.com/reference#roomapi_deleteroom - """ - return self.request([room_id], method='DELETE') - - def get(self, room_id): - """Gets a room by ID. - - Args: - room_id (str, required): The ID of the room to be fetched. - - See Also: - https://developers.dialpad.com/reference#roomapi_getroom - """ - return self.request([room_id], method='GET') - - def update(self, room_id, **kwargs): - """Updates the specified room. - - Args: - room_id (str, required): The ID of the room to be updated. - name (str, optional): A human-readable name for the room. - phone_numbers (list, optional): The list of e164-formatted phone numbers that should be - associated with this room. New numbers will be assigned, - and omitted numbers will be unassigned. - - See Also: - https://developers.dialpad.com/reference#roomapi_updateroom - """ - return self.request([room_id], method='PATCH', data=kwargs) - - def assign_number(self, room_id, **kwargs): - """Assigns a phone number to the specified room - - Args: - room_id (int, required): The ID of the room to which the number should be assigned. - number (str, optional): An e164 number that has already been allocated to the company's - reserved number pool that should be re-assigned to this office. - area_code (str, optional): The area code to use to filter the set of available numbers to be - assigned to this office. - - See Also: - https://developers.dialpad.com/reference#numberapi_assignnumbertoroom - """ - return self.request([room_id, 'assign_number'], method='POST', data=kwargs) - - def unassign_number(self, room_id, number): - """Unassigns the specified number from the specified room. - - Args: - room_id (int, required): The ID of the room. - number (str, required): The e164-formatted number that should be unassigned from the room. - - See Also: - https://developers.dialpad.com/reference#numberapi_unassignnumberfromroom - """ - return self.request([room_id, 'unassign_number'], method='POST', data={'number': number}) - - def get_deskphones(self, room_id): - """Lists the phones that are assigned to the specified room. - - Args: - room_id (int, required): The ID of the room. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_listroomdeskphones - """ - return self.request([room_id, 'deskphones'], method='GET') - - def create_deskphone(self, room_id, mac_address, name, phone_type): - """Creates a desk phone belonging to the specified room. - - Args: - room_id (int, required): The ID of the room. - mac_address (str, required): MAC address of the desk phone. - name (str, required): A human-readable name for the desk phone. - phone_type (str, required): Type (vendor) of the desk phone. One of "obi", "polycom", "sip", - "mini", "audiocodes", "yealink". Use "sip" for generic types. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_createroomdeskphone - """ - return self.request([room_id, 'deskphones'], method='POST', data={ - 'mac_address': mac_address, - 'name': name, - 'type': phone_type, - }) - - def delete_deskphone(self, room_id, deskphone_id): - """Deletes the specified desk phone. - - Args: - room_id (int, required): The ID of the room. - deskphone_id (str, required): The ID of the desk phone. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_deleteroomdeskphone - """ - return self.request([room_id, 'deskphones', deskphone_id], method='DELETE') - - def get_deskphone(self, room_id, deskphone_id): - """Gets the specified desk phone. - - Args: - room_id (int, required): The ID of the room. - deskphone_id (str, required): The ID of the desk phone. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_getroomdeskphone - """ - return self.request([room_id, 'deskphones', deskphone_id], method='GET') diff --git a/dialpad/resources/sms.py b/dialpad/resources/sms.py deleted file mode 100644 index 31a446d..0000000 --- a/dialpad/resources/sms.py +++ /dev/null @@ -1,28 +0,0 @@ -from .resource import DialpadResource - -class SMSResource(DialpadResource): - """SMSResource implements python bindings for the Dialpad API's sms endpoints. - See https://developers.dialpad.com/reference#sms for additional documentation. - """ - _resource_path = ['sms'] - - def send_sms(self, user_id, to_numbers, text, **kwargs): - """Sends an SMS message on behalf of the specified user. - - Args: - user_id (int, required): The ID of the user that should be sending the SMS. - to_numbers (list, required): A list of one-or-more e164-formatted phone numbers which - should receive the SMS. - text (str, required): The content of the SMS message. - infer_country_code (bool, optional): If set, the e164-contraint will be relaxed on to_numbers, - and potentially ambiguous numbers will be assumed to be - numbers in the specified user's country. - sender_group_id (int, optional): The ID of an office, department, or call center that the user - should send the SMS on behalf of. - sender_group_type (str, optional): The ID type (i.e. office, department, or callcenter). - - See Also: - https://developers.dialpad.com/reference#roomapi_listrooms - """ - return self.request(method='POST', data=dict(text=text, user_id=user_id, to_numbers=to_numbers, - **kwargs)) diff --git a/dialpad/resources/stats.py b/dialpad/resources/stats.py deleted file mode 100644 index aefaeb4..0000000 --- a/dialpad/resources/stats.py +++ /dev/null @@ -1,64 +0,0 @@ -from .resource import DialpadResource - -class StatsExportResource(DialpadResource): - """StatsExportResource implements python bindings for the Dialpad API's stats endpoints. - See https://developers.dialpad.com/reference#stats for additional documentation. - """ - _resource_path = ['stats'] - - def post(self, coaching_group=False, days_ago_start=1, days_ago_end=30, is_today=False, - export_type='stats', stat_type='calls', **kwargs): - """Initiate a stats export. - - Args: - coaching_group (bool, optional): Whether or not the the statistics should be for trainees of - the coach with the given target_id. - days_ago_start (int, optional): Start of the date range to get statistics for. This is the - number of days to look back relative to the current day. Used - in conjunction with days_ago_end to specify a range. - days_ago_end (int, optional): End of the date range to get statistics for. This is the number - of days to look back relative to the current day. Used in - conjunction with days_ago_start to specify a range. - is_today (bool, optional): Whether or not the statistics are for the current day. - days_ago_start and days_ago_end are ignored if this is passed in - export_type ("stats" or "records", optional): Whether to return aggregated statistics (stats), - or individual rows for each record (records). - stat_type (str, optional): One of "calls", "texts", "voicemails", "recordings", "onduty", - "csat", "dispositions". The type of statistics to be returned. - office_id (int, optional): ID of the office to get statistics for. If a target_id and - target_type are passed in this value is ignored and instead the - target is used. - target_id (int, optional): The ID of the target for which to return statistics. - target_type (type, optional): One of "department", "office", "callcenter", "user", "room", - "staffgroup", "callrouter", "channel", "coachinggroup", - "unknown". The type corresponding to the target_id. - timezone (str, optional): Timezone using a tz database name. - - See Also: - https://developers.dialpad.com/reference#statsapi_processstats - """ - - data = { - 'coaching_group': coaching_group, - 'days_ago_start': str(days_ago_start), - 'days_ago_end': str(days_ago_end), - 'is_today': is_today, - 'export_type': export_type, - 'stat_type': stat_type, - } - - data.update(kwargs) - - data = {k: v for k, v in data.items() if v is not None} - return self.request(method='POST', data=data) - - def get(self, export_id): - """Retrieves the results of a stats export. - - Args: - export_id (str, required): The export ID returned by the post method. - - See Also: - https://developers.dialpad.com/reference#statsapi_getstats - """ - return self.request([export_id]) diff --git a/dialpad/resources/subscription.py b/dialpad/resources/subscription.py deleted file mode 100644 index 720149f..0000000 --- a/dialpad/resources/subscription.py +++ /dev/null @@ -1,316 +0,0 @@ -from .resource import DialpadResource - -class SubscriptionResource(DialpadResource): - """SubscriptionResource implements python bindings for the Dialpad API's subscription - endpoints. - - See https://developers.dialpad.com/reference#subscriptions for additional documentation. - """ - _resource_path = ['subscriptions'] - - def list_agent_status_event_subscriptions(self, limit=25, **kwargs): - """Lists agent status event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - - See Also: - https://developers.dialpad.com/reference#webhookagentstatuseventsubscriptionapi_listagentstatuseventsubscriptions - """ - return self.request(['agent_status'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_agent_status_event_subscription(self, subscription_id): - """Gets a specific agent status event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookagentstatuseventsubscriptionapi_getagentstatuseventsubscription - """ - return self.request(['agent_status', subscription_id], method='GET') - - def create_agent_status_event_subscription(self, webhook_id, agent_type, enabled=True, **kwargs): - """Create a new agent status event subscription. - - Args: - webhook_id (str, required): The ID of the webhook which should be called when the - subscription fires - agent_type (str, required): The type of agent to subscribe to updates to - enabled (bool, optional): Whether or not the subscription should actually fire - - See Also: - https://developers.dialpad.com/reference#webhookagentstatuseventsubscriptionapi_createagentstatuseventsubscription - """ - - return self.request(['agent_status'], method='POST', - data=dict(webhook_id=webhook_id, enabled=enabled, agent_type=agent_type, - **kwargs)) - - def update_agent_status_event_subscription(self, subscription_id, **kwargs): - """Update an existing agent status event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - webhook_id (str, optional): The ID of the webhook which should be called when the - subscription fires - agent_type (str, optional): The type of agent to subscribe to updates to - enabled (bool, optional): Whether or not the subscription should actually fire - - See Also: - https://developers.dialpad.com/reference#webhookagentstatuseventsubscriptionapi_updateagentstatuseventsubscription - """ - - return self.request(['agent_status', subscription_id], method='PATCH', data=kwargs) - - def delete_agent_status_event_subscription(self, subscription_id): - """Deletes a specific agent status event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookagentstatuseventsubscriptionapi_deleteagentstatuseventsubscription - """ - return self.request(['agent_status', subscription_id], method='DELETE') - - - def list_call_event_subscriptions(self, limit=25, **kwargs): - """Lists call event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#webhookcalleventsubscriptionapi_listcalleventsubscriptions - """ - return self.request(['call'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_call_event_subscription(self, subscription_id): - """Gets a specific call event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookcalleventsubscriptionapi_getcalleventsubscription - """ - return self.request(['call', subscription_id], method='GET') - - def create_call_event_subscription(self, webhook_id, enabled=True, group_calls_only=False, - **kwargs): - """Create a new call event subscription. - - Args: - webhook_id (str, required): The ID of the webhook which should be called when the - subscription fires - enabled (bool, optional): Whether or not the subscription should actually fire - group_calls_only (bool, optional): Whether to limit the subscription to only fire if the call - is a group call - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - call_states (list, optional): The specific types of call events that should trigger the - subscription (any of "preanswer", "calling", "ringing", - "connected", "merged", "hold", "queued", "voicemail", - "eavesdrop", "monitor", "barge", "hangup", "blocked", - "admin", "parked", "takeover", "all", "postcall", - "transcription", or "recording") - - See Also: - https://developers.dialpad.com/reference#webhookcalleventsubscriptionapi_createcalleventsubscription - """ - - return self.request(['call'], method='POST', - data=dict(webhook_id=webhook_id, enabled=enabled, group_calls_only=group_calls_only, - **kwargs)) - - def update_call_event_subscription(self, subscription_id, **kwargs): - """Update an existing call event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - webhook_id (str, optional): The ID of the webhook which should be called when the - subscription fires - enabled (bool, optional): Whether or not the subscription should actually fire - group_calls_only (bool, optional): Whether to limit the subscription to only fire if the call - is a group call - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - call_states (list, optional): The specific types of call events that should trigger the - subscription (any of "preanswer", "calling", "ringing", - "connected", "merged", "hold", "queued", "voicemail", - "eavesdrop", "monitor", "barge", "hangup", "blocked", - "admin", "parked", "takeover", "all", "postcall", - "transcription", or "recording") - - See Also: - https://developers.dialpad.com/reference#webhookcalleventsubscriptionapi_updatecalleventsubscription - """ - return self.request(['call', subscription_id], method='PATCH', data=kwargs) - - def delete_call_event_subscription(self, subscription_id): - """Deletes a specific call event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookcalleventsubscriptionapi_deletecalleventsubscription - """ - return self.request(['call', subscription_id], method='DELETE') - - - def list_contact_event_subscriptions(self, limit=25, **kwargs): - """Lists contact event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - - See Also: - https://developers.dialpad.com/reference#webhookcontacteventsubscriptionapi_listcontacteventsubscriptions - """ - return self.request(['contact'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_contact_event_subscription(self, subscription_id): - """Gets a specific contact event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookcontacteventsubscriptionapi_getcontacteventsubscription - """ - return self.request(['contact', subscription_id], method='GET') - - def create_contact_event_subscription(self, webhook_id, contact_type, enabled=True, **kwargs): - """Create a new contact event subscription. - - Args: - webhook_id (str, required): The ID of the webhook which should be called when the - subscription fires - contact_type (str, required): The type of contact to subscribe to events for - enabled (bool, optional): Whether or not the subscription should actually fire - - See Also: - https://developers.dialpad.com/reference#webhookcontacteventsubscriptionapi_createcontacteventsubscription - """ - - return self.request(['contact'], method='POST', - data=dict(webhook_id=webhook_id, enabled=enabled, - contact_type=contact_type, **kwargs)) - - def update_contact_event_subscription(self, subscription_id, **kwargs): - """Update an existing contact event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - webhook_id (str, optional): The ID of the webhook which should be called when the - subscription fires - contact_type (str, optional): The type of contact to subscribe to events for - enabled (bool, optional): Whether or not the subscription should actually fire - - See Also: - https://developers.dialpad.com/reference#webhookcontacteventsubscriptionapi_updatecontacteventsubscription - """ - return self.request(['contact', subscription_id], method='PATCH', data=kwargs) - - def delete_contact_event_subscription(self, subscription_id): - """Deletes a specific contact event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhookcontacteventsubscriptionapi_deletecontacteventsubscription - """ - return self.request(['contact', subscription_id], method='DELETE') - - - def list_sms_event_subscriptions(self, limit=25, **kwargs): - """Lists SMS event subscriptions. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#webhooksmseventsubscriptionapi_listsmseventsubscriptions - """ - return self.request(['sms'], method='GET', data=dict(limit=limit, **kwargs)) - - def get_sms_event_subscription(self, subscription_id): - """Gets a specific sms event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhooksmseventsubscriptionapi_getsmseventsubscription - """ - return self.request(['sms', subscription_id], method='GET') - - def create_sms_event_subscription(self, webhook_id, direction, enabled=True, **kwargs): - """Create a new SMS event subscription. - - Args: - webhook_id (str, required): The ID of the webhook which should be called when the - subscription fires - direction (str, required): The SMS direction that should fire the subscripion ("inbound", - "outbound", or "all") - enabled (bool, optional): Whether or not the subscription should actually fire - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_createorupdatesmseventsubscription - """ - - return self.request(['sms'], method='POST', - data=dict(webhook_id=webhook_id, enabled=enabled, direction=direction, - **kwargs)) - - def update_sms_event_subscription(self, subscription_id, **kwargs): - """Update an existing SMS event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - webhook_id (str, optional): The ID of the webhook which should be called when the - subscription fires - direction (str, optional): The SMS direction that should fire the subscripion ("inbound", - "outbound", or "all") - enabled (bool, optional): Whether or not the subscription should actually fire - target_id (str, optional): The ID of a specific target to use as a filter - target_type (str, optional): The type of the target (one of "department", "office", - "callcenter", "user", "room", "staffgroup", "callrouter", - "channel", "coachinggroup", or "unknown") - - See Also: - https://developers.dialpad.com/reference#smseventsubscriptionapi_createorupdatesmseventsubscription - """ - - return self.request(['sms', subscription_id], method='PATCH', data=kwargs) - - def delete_sms_event_subscription(self, subscription_id): - """Deletes a specific sms event subscription. - - Args: - subscription_id (str, required): The ID of the subscription - - See Also: - https://developers.dialpad.com/reference#webhooksmseventsubscriptionapi_deletesmseventsubscription - """ - return self.request(['sms', subscription_id], method='DELETE') - diff --git a/dialpad/resources/transcript.py b/dialpad/resources/transcript.py deleted file mode 100644 index 8373af5..0000000 --- a/dialpad/resources/transcript.py +++ /dev/null @@ -1,18 +0,0 @@ -from .resource import DialpadResource - -class TranscriptResource(DialpadResource): - """TranscriptResource implements python bindings for the Dialpad API's transcript endpoints. - See https://developers.dialpad.com/reference#transcripts for additional documentation. - """ - _resource_path = ['transcripts'] - - def get(self, call_id): - """Get the transcript of a call. - - Args: - call_id (int, required): The ID of the call. - - See Also: - https://developers.dialpad.com/reference#transcriptapi_gettranscript - """ - return self.request([call_id]) diff --git a/dialpad/resources/user.py b/dialpad/resources/user.py deleted file mode 100644 index 5a0962f..0000000 --- a/dialpad/resources/user.py +++ /dev/null @@ -1,263 +0,0 @@ -from .resource import DialpadResource - -class UserResource(DialpadResource): - """UserResource implements python bindings for the Dialpad API's user endpoints. - See https://developers.dialpad.com/reference#users for additional documentation. - """ - _resource_path = ['users'] - - def list(self, limit=25, **kwargs): - """Lists users in the company. - - Args: - email (str, optional): Limits results to users with a matching email address. - state (str, optional): Limits results to users in the specified state (one of "active", - "canceled", "suspended", "pending", "deleted", "all) - limit (int, optional): The number of users to fetch per request. - - See Also: - https://developers.dialpad.com/reference#userapi_listusers - """ - return self.request(data=dict(limit=limit, **kwargs)) - - def create(self, email, office_id, **kwargs): - """Creates a new user. - - Args: - email (str, required): The user's email address. - office_id (int, required): The ID of the office that the user should belong to. - first_name (str, optional): The first name of the user. - last_name (str, optional): The last name of the user. - license (str, optional): The license that the user should be created with. (One of "talk", - "agents", "lite_support_agents", "lite_lines") - - See Also: - https://developers.dialpad.com/reference#userapi_createuser - """ - return self.request(method='POST', data=dict(email=email, office_id=office_id, **kwargs)) - - def delete(self, user_id): - """Deletes a user. - - Args: - user_id (int, required): The ID of the user to delete. - - See Also: - https://developers.dialpad.com/reference#userapi_deleteuser - """ - return self.request([user_id], method='DELETE') - - def get(self, user_id): - """Gets a user by ID. - - Args: - user_id (int, required): The ID of the user. - - See Also: - https://developers.dialpad.com/reference#userapi_getuser - """ - return self.request([user_id]) - - def update(self, user_id, **kwargs): - """Updates a user by ID. - - Note: - The "phone_numbers" argument can be used to re-order or unassign numbers, but it cannot be - used to assign new numbers. To assign new numbers to a user, please use the number assignment - API instead. - - Args: - user_id (int, required): The ID of the user. - admin_office_ids (list, optional): The office IDs that this user should be an admin of. - emails (list, optional): The email addresses that should be assoiciated with this user. - extension (str, optional): The extension that this user can be reached at. - first_name (str, optional): The first name of the user. - last_name (str, optional): The last name of the user. - forwarding_numbers (list, optional): The e164-formatted numbers that should also ring - when the user receives a Dialpad call. - is_super_admin (bool, optional): Whether this user should be a company-level admin. - job_title (str, optional): The user's job title. - license (str, optional): The user's license type. Changing this affects billing for the user. - office_id (int, optional): The ID of office to which this user should belong. - phone_numbers (list, optional): The e164-formatted numbers that should be assigned to - this user. - state (str, optional): The state of the user (One of "suspended", "active") - - See Also: - https://developers.dialpad.com/reference#userapi_updateuser - """ - return self.request([user_id], method='PATCH', data=kwargs) - - def toggle_call_recording(self, user_id, **kwargs): - """Turn call recording on or off for a user's active call. - - Args: - user_id (int, required): The ID of the user. - is_recording (bool, optional): Whether recording should be turned on. - play_message (bool, optional): Whether a message should be played to the user to notify them - that they are now being (or no longer being) recorded. - recording_type (str, optional): One of "user", "group", or "all". If set to "user", then only - the user's individual calls will be recorded. If set to - "group", then only calls in which the user is acting as an - operator will be recorded. If set to "all", then all of the - user's calls will be recorded. - - See Also: - https://developers.dialpad.com/reference#callapi_updateactivecall - """ - return self.request([user_id, 'activecall'], method='PATCH', data=kwargs) - - def assign_number(self, user_id, **kwargs): - """Assigns a new number to the user. - - Args: - user_id (int, required): The ID of the user to which the number should be assigned. - number (str, optional): An e164 number that has already been allocated to the company's - reserved number pool that should be re-assigned to this user. - area_code (str, optional): The area code to use to filter the set of available numbers to be - assigned to this user. - - See Also: - https://developers.dialpad.com/reference#numberapi_assignnumbertouser - """ - return self.request([user_id, 'assign_number'], method='POST', data=kwargs) - - def initiate_call(self, user_id, phone_number, **kwargs): - """Causes a user's native Dialpad application to initiate an outbound call. - - Args: - user_id (int, required): The ID of the user. - phone_number (str, required): The e164-formatted number to call. - custom_data (str, optional): free-form extra data to associate with the call. - group_id (str, optional): The ID of a group that will be used to initiate the call. - group_type (str, optional): The type of a group that will be used to initiate the call. - outbound_caller_id (str, optional): The e164-formatted number shown to the call recipient - (or "blocked"). If set to "blocked", the recipient will receive a - call from "unknown caller". - - See Also: - https://developers.dialpad.com/reference#callapi_initiatecall - """ - data = { - 'phone_number': phone_number - } - for k in ['group_id', 'group_type', 'outbound_caller_id', 'custom_data']: - if k in kwargs: - data[k] = kwargs.pop(k) - assert not kwargs - return self.request([user_id, 'initiate_call'], method='POST', data=data) - - def unassign_number(self, user_id, number): - """Unassigns the specified number from the specified user. - - Args: - user_id (int, required): The ID of the user. - number (str, required): The e164-formatted number that should be unassigned from the user. - - See Also: - https://developers.dialpad.com/reference#numberapi_unassignnumberfromuser - """ - return self.request([user_id, 'unassign_number'], method='POST', data={'number': number}) - - def get_deskphones(self, user_id): - """Lists the desk phones that are associated with a user. - - Args: - user_id (int, required): The ID of the user. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_listuserdeskphones - """ - return self.request([user_id, 'deskphones'], method='GET') - - def create_deskphone(self, user_id, mac_address, name, phone_type): - """Creates a desk phone belonging to the specified user. - - Args: - user_id (int, required): The ID of the user. - mac_address (str, required): MAC address of the desk phone. - name (str, required): A human-readable name for the desk phone. - phone_type (str, required): Type (vendor) of the desk phone. One of "obi", "polycom", "sip", - "mini", "audiocodes", "yealink". Use "sip" for generic types. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_createuserdeskphone - """ - return self.request([user_id, 'deskphones'], method='POST', data={ - 'mac_address': mac_address, - 'name': name, - 'type': phone_type, - }) - - def delete_deskphone(self, user_id, deskphone_id): - """Deletes the specified desk phone. - - Args: - user_id (int, required): The ID of the user. - deskphone_id (str, required): The ID of the desk phone. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_deleteuserdeskphone - """ - return self.request([user_id, 'deskphones', deskphone_id], method='DELETE') - - def get_deskphone(self, user_id, deskphone_id): - """Gets the specified desk phone. - - Args: - user_id (int, required): The ID of the user. - deskphone_id (str, required): The ID of the desk phone. - - See Also: - https://developers.dialpad.com/reference#deskphoneapi_getuserdeskphone - """ - return self.request([user_id, 'deskphones', deskphone_id], method='GET') - - def get_personas(self, user_id): - """Lists the calling personas that are associated with a user. - - Args: - user_id (int, required): The ID of the user. - - See Also: - https://developers.dialpad.com/reference#users - """ - return self.request([user_id, 'personas'], method='GET') - - def toggle_do_not_disturb(self, user_id, do_not_disturb): - """Toggle DND status on or off for the given user. - - Args: - user_id (int, required): The ID of the user. - do_not_disturb (bool, required): A boolean indicating whether to enable or disable the - "do not disturb" setting. - - See Also: - https://developers.dialpad.com/reference/userapi_togglednd - """ - return self.request([user_id, 'togglednd'], method='PATCH', - data={'do_not_disturb': do_not_disturb}) - - def search(self, query, **kwargs): - """User -- Search - - Searches for users matching a specific criteria. It matches phone numbers, emails, or name. - Optionally, it accepts filters to reduce the amount of final results. - - - The `cursor` value is provided in the API response, and can be passed as a parameter to - retrieve subsequent pages of results. - - Args: - query (str, required): A string that will be matched against user information. For phone - numbers in e164 format, it is recommended to URL-encode the model - term. - cursor (str, optional): A token used to return the next page of a previous request. Use the - cursor provided in the previous response. - filter (str, optional): If provided, query will be performed against a smaller set of data. - Format for providing filters is in the form of an array of key=value - pairs. (i.e. filter=[key=value]) - - See Also: - https://developers.dialpad.com/reference/searchusers - """ - return self.request(['search'], method='GET', data=dict(query=query, **kwargs)) diff --git a/dialpad/resources/userdevice.py b/dialpad/resources/userdevice.py deleted file mode 100644 index b36e0e5..0000000 --- a/dialpad/resources/userdevice.py +++ /dev/null @@ -1,30 +0,0 @@ -from .resource import DialpadResource - -class UserDeviceResource(DialpadResource): - """UserDeviceResource implements python bindings for the Dialpad API's userdevice endpoints. - See https://developers.dialpad.com/reference#userdevices for additional documentation. - """ - _resource_path = ['userdevices'] - - def get(self, device_id): - """Gets a user device by ID. - - Args: - device_id (str, required): The ID of the device. - - See Also: - https://developers.dialpad.com/reference#userdeviceapi_getdevice - """ - return self.request([device_id]) - - def list(self, user_id, limit=25, **kwargs): - """Lists the devices for a specific user. - - Args: - user_id (int, required): The ID of the user. - limit (int, optional): the number of devices to fetch per request. - - See Also: - https://developers.dialpad.com/reference#userdeviceapi_listuserdevices - """ - return self.request(data=dict(user_id=user_id, limit=limit, **kwargs)) diff --git a/dialpad/resources/webhook.py b/dialpad/resources/webhook.py deleted file mode 100644 index bcb1c5e..0000000 --- a/dialpad/resources/webhook.py +++ /dev/null @@ -1,66 +0,0 @@ -from .resource import DialpadResource - -class WebhookResource(DialpadResource): - """WebhookResource implements python bindings for the Dialpad API's webhook endpoints. - - See https://developers.dialpad.com/reference#webhooks for additional documentation. - """ - _resource_path = ['webhooks'] - - def list_webhooks(self, limit=25, **kwargs): - """Lists all webhooks. - - Args: - limit (int, optional): The number of subscriptions to fetch per request - - See Also: - https://developers.dialpad.com/reference#webhookapi_listwebhooks - """ - return self.request(method='GET', data=dict(limit=limit, **kwargs)) - - def get_webhook(self, webhook_id): - """Gets a specific webhook. - - Args: - webhook_id (str, required): The ID of the webhook - - See Also: - https://developers.dialpad.com/reference#webhookapi_getwebhook - """ - return self.request([webhook_id], method='GET') - - def create_webhook(self, hook_url, **kwargs): - """Creates a new webhook. - - Args: - hook_url (str, required): The URL which should be called when subscriptions fire - secret (str, optional): A secret to use to encrypt subscription event payloads - - See Also: - https://developers.dialpad.com/reference#webhookapi_createwebhook - """ - return self.request(method='POST', data=dict(hook_url=hook_url, **kwargs)) - - def update_webhook(self, webhook_id, **kwargs): - """Updates a specific webhook - - Args: - webhook_id (str, required): The ID of the webhook - hook_url (str, optional): The URL which should be called when subscriptions fire - secret (str, optional): A secret to use to encrypt subscription event payloads - - See Also: - https://developers.dialpad.com/reference#webhookapi_updatewebhook - """ - return self.request([webhook_id], method='PATCH', data=kwargs) - - def delete_webhook(self, webhook_id): - """Deletes a specific webhook. - - Args: - webhook_id (str, required): The ID of the webhook - - See Also: - https://developers.dialpad.com/reference#webhookapi_deletewebhook - """ - return self.request([webhook_id], method='DELETE') diff --git a/dialpad_api_spec.json b/dialpad_api_spec.json new file mode 100644 index 0000000..eebfa34 --- /dev/null +++ b/dialpad_api_spec.json @@ -0,0 +1,20449 @@ +{ + "components": { + "schemas": { + "schemas.oauth.AuthorizationCodeGrantBodySchema": { + "description": "Used to redeem an access token via authorization code.", + "properties": { + "client_id": { + "description": "The client_id of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.", + "nullable": true, + "type": "string" + }, + "client_secret": { + "description": "The client_secret of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.", + "nullable": true, + "type": "string" + }, + "code": { + "description": "The authorization code that resulted from the oauth2 authorization redirect.", + "nullable": true, + "type": "string" + }, + "code_verifier": { + "description": "The PKCE code verifier corresponding to the initial PKCE code challenge, if applicable.", + "nullable": true, + "type": "string" + }, + "grant_type": { + "description": "The type of OAuth grant which is being requested.", + "enum": [ + "authorization_code" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "code", + "grant_type" + ], + "title": "Authorization Code Grant", + "type": "object" + }, + "schemas.oauth.AuthorizeTokenResponseBodySchema": { + "properties": { + "access_token": { + "description": "A static access token.", + "nullable": true, + "type": "string" + }, + "expires_in": { + "description": "The number of seconds after which the access token will become expired.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "id_token": { + "description": "User ID token (if using OpenID Connect)", + "nullable": true, + "type": "string" + }, + "refresh_token": { + "description": "The refresh token that can be used to obtain a new token pair when this one expires.", + "nullable": true, + "type": "string" + }, + "token_type": { + "description": "The type of the access_token being issued.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.oauth.RefreshTokenGrantBodySchema": { + "description": "Used to exchange a refresh token for a short-lived access token and another refresh token.", + "properties": { + "client_id": { + "description": "The client_id of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.", + "nullable": true, + "type": "string" + }, + "client_secret": { + "description": "The client_secret of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.", + "nullable": true, + "type": "string" + }, + "grant_type": { + "description": "The type of OAuth grant which is being requested.", + "enum": [ + "refresh_token" + ], + "nullable": true, + "type": "string" + }, + "refresh_token": { + "description": "The current refresh token which is being traded in for a new token pair.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "grant_type", + "refresh_token" + ], + "title": "Refresh Token Grant", + "type": "object" + }, + "schemas.access_control_policies.AssignmentPolicyMessage": { + "properties": { + "target_id": { + "description": "Required if the policy is associated with a target (Office or Contact Center). Not required for a company level policy.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "default": "company", + "description": "Policy permissions applied at this target level. Defaults to company target type.", + "enum": [ + "callcenter", + "company", + "office" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The user's id to be assigned to the policy.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "title": "Policy assignment message.", + "type": "object" + }, + "schemas.access_control_policies.CreatePolicyMessage": { + "properties": { + "description": { + "description": "[single-line only]\n\nOptional description for the policy. Max 200 characters.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the policy. Max 50 characters.", + "nullable": true, + "type": "string" + }, + "owner_id": { + "description": "Owner for this policy i.e company admin.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "permission_sets": { + "description": "List of permission associated with this policy.", + "items": { + "enum": [ + "agent_settings_write", + "agents_admins_manage_agents_settings_write", + "agents_admins_skill_level_write", + "auto_call_recording_and_transcription_settings_write", + "business_hours_write", + "call_blocking_spam_prevention_settings_write", + "call_dispositions_settings_write", + "call_routing_hours_settings_write", + "cc_call_settings_write", + "chrome_extension_compliance_settings_write", + "csat_surveys_write", + "dashboard_and_alerts_write", + "dialpad_ai_settings_write", + "holiday_hours_settings_write", + "integrations_settings_write", + "name_language_description_settings_write", + "number_settings_write", + "supervisor_settings_write" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "target_type": { + "default": "company", + "description": "Policy permissions applied at this target level. Defaults to company target type.", + "enum": [ + "callcenter", + "company", + "office" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "name", + "owner_id", + "permission_sets" + ], + "title": "Create access control policy message.", + "type": "object" + }, + "schemas.access_control_policies.PoliciesCollection": { + "properties": { + "cursor": { + "description": "A cursor string that can be used to fetch the subsequent page.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list containing the first page of results.", + "items": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of custom policies.", + "type": "object" + }, + "schemas.access_control_policies.PolicyAssignmentCollection": { + "properties": { + "cursor": { + "description": "A cursor string that can be used to fetch the subsequent page.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list containing the first page of results.", + "items": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyAssignmentProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of policy assignments.", + "type": "object" + }, + "schemas.access_control_policies.PolicyAssignmentProto": { + "properties": { + "policy_targets": { + "description": "Policy targets associated with the role.", + "items": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyTargetProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "user": { + "$ref": "#/components/schemas/schemas.user.UserProto", + "description": "The user associated to the role.", + "nullable": true, + "type": "object" + } + }, + "type": "object" + }, + "schemas.access_control_policies.PolicyProto": { + "properties": { + "company_id": { + "description": "The company's id to which this policy belongs to.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "date_created": { + "description": "A timestamp indicating when this custom policy was created.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_updated": { + "description": "A timestamp indicating when this custom policy was last modified.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "description": { + "description": "[single-line only]\n\nDescription for the custom policy.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The API custom policy ID.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the custom policy name.", + "nullable": true, + "type": "string" + }, + "owner_id": { + "description": "Target that created this policy i.e company admin.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "permission_sets": { + "description": "List of permission associated with this custom policy.", + "items": { + "enum": [ + "agent_settings_read", + "agent_settings_write", + "agents_admins_manage_agents_settings_read", + "agents_admins_manage_agents_settings_write", + "agents_admins_skill_level_read", + "agents_admins_skill_level_write", + "auto_call_recording_and_transcription_settings_read", + "auto_call_recording_and_transcription_settings_write", + "business_hours_read", + "business_hours_write", + "call_blocking_spam_prevention_settings_read", + "call_blocking_spam_prevention_settings_write", + "call_dispositions_settings_read", + "call_dispositions_settings_write", + "call_routing_hours_settings_read", + "call_routing_hours_settings_write", + "cc_call_settings_read", + "cc_call_settings_write", + "chrome_extension_compliance_settings_read", + "chrome_extension_compliance_settings_write", + "csat_surveys_read", + "csat_surveys_write", + "dashboard_and_alerts_read", + "dashboard_and_alerts_write", + "dialpad_ai_settings_read", + "dialpad_ai_settings_write", + "holiday_hours_settings_read", + "holiday_hours_settings_write", + "integrations_settings_read", + "integrations_settings_write", + "name_language_description_settings_read", + "name_language_description_settings_write", + "number_settings_read", + "number_settings_write", + "supervisor_settings_read", + "supervisor_settings_write" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "Policy state. ex. active or deleted.", + "enum": [ + "active", + "deleted" + ], + "nullable": true, + "type": "string" + }, + "target_type": { + "description": "Target level at which the policy permissions are applied. Defaults to company", + "enum": [ + "callcenter", + "company", + "office" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "company_id", + "id", + "name", + "owner_id", + "permission_sets", + "state" + ], + "title": "API custom access control policy proto definition.", + "type": "object" + }, + "schemas.access_control_policies.PolicyTargetProto": { + "properties": { + "target_id": { + "description": "All targets associated with the policy.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "default": "company", + "description": "Policy permissions applied at this target level. Defaults to company target type.", + "enum": [ + "callcenter", + "company", + "office" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id" + ], + "type": "object" + }, + "schemas.access_control_policies.UnassignmentPolicyMessage": { + "properties": { + "target_id": { + "description": "Required if the policy is associated with a target (Office or Contact Center). Not required for a company level policy or if unassign_all is True.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "default": "company", + "description": "Policy permissions applied at this target level. Defaults to company target type.", + "enum": [ + "callcenter", + "company", + "office" + ], + "nullable": true, + "type": "string" + }, + "unassign_all": { + "default": false, + "description": "Unassign all associated target groups from the user for a policy.", + "nullable": true, + "type": "boolean" + }, + "user_id": { + "description": "The user's id to be assigned to the policy.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "title": "Policy unassignment message.", + "type": "object" + }, + "schemas.access_control_policies.UpdatePolicyMessage": { + "properties": { + "description": { + "description": "[single-line only]\n\nOptional description for the policy.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the policy.", + "nullable": true, + "type": "string" + }, + "permission_sets": { + "description": "List of permission associated with this policy.", + "items": { + "enum": [ + "agent_settings_write", + "agents_admins_manage_agents_settings_write", + "agents_admins_skill_level_write", + "auto_call_recording_and_transcription_settings_write", + "business_hours_write", + "call_blocking_spam_prevention_settings_write", + "call_dispositions_settings_write", + "call_routing_hours_settings_write", + "cc_call_settings_write", + "chrome_extension_compliance_settings_write", + "csat_surveys_write", + "dashboard_and_alerts_write", + "dialpad_ai_settings_write", + "holiday_hours_settings_write", + "integrations_settings_write", + "name_language_description_settings_write", + "number_settings_write", + "supervisor_settings_write" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "Restore a deleted policy.", + "enum": [ + "active" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "user id updating this policy. Must be a company admin", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Update policy message.", + "type": "object" + }, + "schemas.agent_status_event_subscription.AgentStatusEventSubscriptionCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of SMS event subscriptions.", + "items": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of agent status event subscriptions.", + "type": "object" + }, + "schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto": { + "properties": { + "agent_type": { + "description": "The agent type this event subscription subscribes to.", + "enum": [ + "callcenter" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the this agent status event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "required": [ + "agent_type" + ], + "title": "Agent-status event subscription.", + "type": "object" + }, + "schemas.agent_status_event_subscription.CreateAgentStatusEventSubscription": { + "properties": { + "agent_type": { + "description": "The agent type this event subscription subscribes to.", + "enum": [ + "callcenter" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the this agent status event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "agent_type" + ], + "type": "object" + }, + "schemas.agent_status_event_subscription.UpdateAgentStatusEventSubscription": { + "properties": { + "agent_type": { + "default": "callcenter", + "description": "The agent type this event subscription subscribes to.", + "enum": [ + "callcenter" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the this agent status event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.app.setting.AppSettingProto": { + "properties": { + "enabled": { + "default": false, + "description": "Whether or not the OAuth app is enabled for the target.", + "nullable": true, + "type": "boolean" + }, + "is_preferred_service": { + "default": false, + "description": "Whether or not Oauth app is preferred service for screen pop.", + "nullable": true, + "type": "boolean" + }, + "settings": { + "description": "A dynamic object that maps settings to their values.\n\nIt includes all standard settings, i.e. call_logging_enabled, call_recording_logging_enabled,\nvoicemail_logging_enabled and sms_logging_enabled, and any custom settings this OAuth app supports.", + "nullable": true, + "type": "object" + } + }, + "title": "App settings object.", + "type": "object" + }, + "schemas.blocked_number.AddBlockedNumbersProto": { + "properties": { + "numbers": { + "description": "A list of E164 formatted numbers.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.blocked_number.BlockedNumber": { + "properties": { + "number": { + "description": "A phone number (e164 format).", + "nullable": true, + "type": "string" + } + }, + "title": "Blocked number.", + "type": "object" + }, + "schemas.blocked_number.BlockedNumberCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of blocked numbers.", + "items": { + "$ref": "#/components/schemas/schemas.blocked_number.BlockedNumber", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of blocked numbers.", + "type": "object" + }, + "schemas.blocked_number.RemoveBlockedNumbersProto": { + "properties": { + "numbers": { + "description": "A list of E164 formatted numbers.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.breadcrumbs.ApiCallRouterBreadcrumb": { + "properties": { + "breadcrumb_type": { + "description": "Breadcrumb type", + "enum": [ + "callrouter", + "external_api" + ], + "nullable": true, + "type": "string" + }, + "date": { + "description": "Date when this breadcrumb was added", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "request": { + "description": "The HTTP request payload associated with this breadcrumb", + "nullable": true, + "type": "object" + }, + "response": { + "description": "The HTTP response associated with this breadcrumb", + "nullable": true, + "type": "object" + }, + "target_id": { + "description": "The target id", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target type from call", + "nullable": true, + "type": "string" + }, + "url": { + "description": "The URL that should be used to drive call routing decisions.", + "nullable": true, + "type": "string" + } + }, + "title": "Call routing breadcrumb.", + "type": "object" + }, + "schemas.call.ActiveCallProto": { + "properties": { + "call_state": { + "description": "The current state of the call.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "A unique number ID automatically assigned to each call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "is_recording": { + "description": "A boolean indicating whether the call is currently being recorded.", + "nullable": true, + "type": "boolean" + } + }, + "title": "Active call.", + "type": "object" + }, + "schemas.call.AddCallLabelsMessage": { + "properties": { + "labels": { + "description": "The list of labels to attach to the call", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Create labels for a call", + "type": "object" + }, + "schemas.call.AddParticipantMessage": { + "properties": { + "participant": { + "description": "New member of the call to add. Can be a number or a Target. In case of a target, it must have a primary number assigned.", + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/schemas.call.NumberTransferDestination" + }, + { + "$ref": "#/components/schemas/schemas.call.TargetTransferDestination" + } + ] + } + }, + "required": [ + "participant" + ], + "title": "Add participant into a Call.", + "type": "object" + }, + "schemas.call.CallCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of calls.", + "items": { + "$ref": "#/components/schemas/schemas.call.CallProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of calls.", + "type": "object" + }, + "schemas.call.CallContactProto": { + "properties": { + "email": { + "description": "The primary email address of the contact.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "A unique number ID for the contact.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nName of contact.", + "nullable": true, + "type": "string" + }, + "phone": { + "description": "The primary phone number of the contact.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Type of the contact.", + "nullable": true, + "type": "string" + } + }, + "title": "Call contact.", + "type": "object" + }, + "schemas.call.CallProto": { + "properties": { + "admin_call_recording_share_links": { + "description": "A list of admin call recording share links.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "call_id": { + "description": "A unique number ID automatically assigned to each call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "call_recording_share_links": { + "description": "A list of call recording share links.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "contact": { + "$ref": "#/components/schemas/schemas.call.CallContactProto", + "description": "This is the contact involved in the call.", + "nullable": true, + "type": "object" + }, + "csat_recording_urls": { + "description": "A list of CSAT urls related to the call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "csat_score": { + "description": "CSAT score related to the call.", + "nullable": true, + "type": "string" + }, + "csat_transcriptions": { + "description": "A list of CSAT texts related to the call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "custom_data": { + "description": "Any custom data.", + "nullable": true, + "type": "string" + }, + "date_connected": { + "description": "Timestamp when Dialpad connected the call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "date_ended": { + "description": "Timestamp when the call was hung up.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "date_rang": { + "description": "Timestamp when Dialpad first detects an inbound call toa mainline, department, or person.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "date_started": { + "description": "Timestamp when the call began in the Dialpad system before being connected.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "direction": { + "description": "Call direction. Indicates whether a call was outbound or inbound.", + "nullable": true, + "type": "string" + }, + "duration": { + "description": "Duration of the call in milliseconds.", + "format": "double", + "nullable": true, + "type": "number" + }, + "entry_point_call_id": { + "description": "Call ID of the associated entry point call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "entry_point_target": { + "$ref": "#/components/schemas/schemas.call.CallContactProto", + "description": "Where a call initially dialed for inbound calls to Dialpad.", + "nullable": true, + "type": "object" + }, + "event_timestamp": { + "description": "Timestamp of when this call event happened.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "external_number": { + "description": "The phone number external to your organization.", + "nullable": true, + "type": "string" + }, + "group_id": { + "description": "Unique ID of the department, mainline, or call queue associated with the call.", + "nullable": true, + "type": "string" + }, + "internal_number": { + "description": "The phone number internal to your organization.", + "nullable": true, + "type": "string" + }, + "is_transferred": { + "description": "Boolean indicating whether or not the call was transferred.", + "nullable": true, + "type": "boolean" + }, + "labels": { + "description": "The label's associated to this call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "master_call_id": { + "description": "The master id of the specified call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "mos_score": { + "description": "Mean Opinion Score", + "format": "double", + "nullable": true, + "type": "number" + }, + "operator_call_id": { + "description": "The id of operator.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "proxy_target": { + "$ref": "#/components/schemas/schemas.call.CallContactProto", + "description": "Caller ID used by the Dialpad user for outbound calls.", + "nullable": true, + "type": "object" + }, + "recording_details": { + "description": "List of associated recording details.", + "items": { + "$ref": "#/components/schemas/schemas.call.CallRecordingDetailsProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "routing_breadcrumbs": { + "description": "The routing breadcrumbs", + "items": { + "$ref": "#/components/schemas/schemas.breadcrumbs.ApiCallRouterBreadcrumb", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "screen_recording_urls": { + "description": "A list of screen recording urls.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "Current call state.", + "nullable": true, + "type": "string" + }, + "target": { + "$ref": "#/components/schemas/schemas.call.CallContactProto", + "description": "This is the target that the Dialpad user dials or receives a call from.", + "nullable": true, + "type": "object" + }, + "total_duration": { + "description": "Duration of the call in milliseconds, including ring time.", + "format": "double", + "nullable": true, + "type": "number" + }, + "transcription_text": { + "description": "Text of call transcription.", + "nullable": true, + "type": "string" + }, + "voicemail_share_link": { + "description": "Share link to the voicemail recording.", + "nullable": true, + "type": "string" + }, + "was_recorded": { + "description": "Boolean indicating whether or not the call was recorded.", + "nullable": true, + "type": "boolean" + } + }, + "title": "Call.", + "type": "object" + }, + "schemas.call.CallRecordingDetailsProto": { + "properties": { + "duration": { + "description": "The duration of the recording in milliseconds", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "id": { + "description": "The recording ID", + "nullable": true, + "type": "string" + }, + "recording_type": { + "description": "The recording type", + "enum": [ + "admincallrecording", + "callrecording", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "start_time": { + "description": "The recording start timestamp", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "url": { + "description": "The access URL of the recording", + "nullable": true, + "type": "string" + } + }, + "title": "Call recording details.", + "type": "object" + }, + "schemas.call.CallTransferDestination": { + "properties": { + "call_id": { + "description": "The id of the ongoing call which the call should be transferred to.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "call_id" + ], + "type": "object" + }, + "schemas.call.CallbackMessage": { + "properties": { + "call_center_id": { + "description": "The ID of a call center that will be used to fulfill the callback.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_number": { + "description": "The e164-formatted number to call back", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call.CallbackProto": { + "description": "Note: Position indicates the new callback request's position in the queue, with 1 being at the front.", + "properties": { + "position": { + "description": "Indicates the new callback request's position in the queue, with 1 being at the front.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Callback.", + "type": "object" + }, + "schemas.call.InitiateCallMessage": { + "properties": { + "custom_data": { + "description": "Extra data to associate with the call. This will be passed through to any subscribed call events.", + "nullable": true, + "type": "string" + }, + "group_id": { + "description": "The ID of a group that will be used to initiate the call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_type": { + "description": "The type of a group that will be used to initiate the call.", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + }, + "outbound_caller_id": { + "description": "The e164-formatted number shown to the call recipient (or \"blocked\").\n\nIf set to \"blocked\", the recipient will receive a call from \"unknown caller\". The number can be the caller's number, or the caller's group number if the group is provided,\nor the caller's company reserved number.", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The e164-formatted number to call.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call.InitiatedCallProto": { + "properties": { + "device": { + "$ref": "#/components/schemas/schemas.userdevice.UserDeviceProto", + "description": "The device used to initiate the call.", + "nullable": true, + "type": "object" + } + }, + "title": "Initiated call.", + "type": "object" + }, + "schemas.call.InitiatedIVRCallProto": { + "properties": { + "call_id": { + "description": "The ID of the initiated call.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "call_id" + ], + "title": "Initiated IVR call.", + "type": "object" + }, + "schemas.call.NumberTransferDestination": { + "properties": { + "number": { + "description": "The phone number which the call should be transferred to.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "number" + ], + "type": "object" + }, + "schemas.call.OutboundIVRMessage": { + "properties": { + "custom_data": { + "description": "Extra data to associate with the call. This will be passed through to any subscribed call events.", + "nullable": true, + "type": "string" + }, + "outbound_caller_id": { + "description": "The e164-formatted number shown to the call recipient (or \"blocked\").", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The e164-formatted number to call.", + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The ID of a group that will be used to initiate the call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of a group that will be used to initiate the call.", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "phone_number", + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.call.RingCallMessage": { + "properties": { + "custom_data": { + "description": "Extra data to associate with the call. This will be passed through to any subscribed call events.", + "nullable": true, + "type": "string" + }, + "device_id": { + "description": "The device's id.", + "nullable": true, + "type": "string" + }, + "group_id": { + "description": "The ID of a group that will be used to initiate the call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_type": { + "description": "The type of a group that will be used to initiate the call.", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + }, + "is_consult": { + "default": false, + "description": "Enables the creation of a second call. If there is an ongoing call, it puts it on hold.", + "nullable": true, + "type": "boolean" + }, + "outbound_caller_id": { + "description": "The e164-formatted number shown to the call recipient (or \"blocked\").\n\nIf set to \"blocked\", the recipient will receive a call from \"unknown caller\". The number can be the caller's number, or the caller's group number if the group is provided, or the caller's company reserved number.", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The e164-formatted number to call.", + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The id of the user who should make the outbound call.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "phone_number", + "user_id" + ], + "type": "object" + }, + "schemas.call.RingCallProto": { + "properties": { + "call_id": { + "description": "The ID of the created call.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Ringing call.", + "type": "object" + }, + "schemas.call.TargetTransferDestination": { + "properties": { + "target_id": { + "description": "The ID of the target that will be used to transfer the call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Type of target that will be used to transfer the call.", + "enum": [ + "callcenter", + "department", + "office", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.call.ToggleViMessage": { + "properties": { + "enable_vi": { + "description": "Whether or not call vi should be enabled.", + "nullable": true, + "type": "boolean" + }, + "vi_locale": { + "description": "The locale to use for vi.", + "enum": [ + "en-au", + "en-ca", + "en-de", + "en-fr", + "en-gb", + "en-it", + "en-jp", + "en-mx", + "en-nl", + "en-nz", + "en-pt", + "en-us", + "es-au", + "es-ca", + "es-de", + "es-es", + "es-fr", + "es-gb", + "es-it", + "es-jp", + "es-mx", + "es-nl", + "es-nz", + "es-pt", + "es-us", + "fr-au", + "fr-ca", + "fr-de", + "fr-es", + "fr-fr", + "fr-gb", + "fr-it", + "fr-jp", + "fr-mx", + "fr-nl", + "fr-nz", + "fr-pt", + "fr-us" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call.ToggleViProto": { + "properties": { + "call_state": { + "description": "Current call state.", + "nullable": true, + "type": "string" + }, + "enable_vi": { + "description": "Whether vi is enabled.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The id of the toggled call.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "vi_locale": { + "description": "The locale used for vi.", + "nullable": true, + "type": "string" + } + }, + "title": "VI state.", + "type": "object" + }, + "schemas.call.TransferCallMessage": { + "properties": { + "custom_data": { + "description": "Extra data to associate with the call. This will be passed through to any subscribed call events.", + "nullable": true, + "type": "string" + }, + "to": { + "description": "Destination of the call that will be transfer. It can be a single option between a number, \nan existing call or a target", + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/schemas.call.CallTransferDestination" + }, + { + "$ref": "#/components/schemas/schemas.call.NumberTransferDestination" + }, + { + "$ref": "#/components/schemas/schemas.call.TargetTransferDestination" + } + ] + }, + "transfer_state": { + "description": "The state which the call should take when it's transferred to.", + "enum": [ + "hold", + "parked", + "preanswer", + "voicemail" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call.TransferredCallProto": { + "properties": { + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "transferred_to_number": { + "description": "The phone number which the call has been transferred to.", + "nullable": true, + "type": "string" + }, + "transferred_to_state": { + "description": "The state which the call has been transferred to.", + "enum": [ + "hold", + "parked", + "preanswer", + "voicemail" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Transferred call.", + "type": "object" + }, + "schemas.call.UnparkCallMessage": { + "properties": { + "user_id": { + "description": "The id of the user who should unpark the call.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "type": "object" + }, + "schemas.call.UpdateActiveCallMessage": { + "properties": { + "is_recording": { + "description": "Whether or not recording should be enabled.", + "nullable": true, + "type": "boolean" + }, + "play_message": { + "default": true, + "description": "Whether or not to play a message to indicate the call is being recorded (or recording has stopped).", + "nullable": true, + "type": "boolean" + }, + "recording_type": { + "default": "user", + "description": "Whether or not to toggle recording for the operator call (personal recording),\nthe group call (department recording), or both.\n\nOnly applicable for group calls (call centers, departments, etc.)", + "enum": [ + "all", + "group", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call.ValidateCallbackProto": { + "properties": { + "success": { + "description": "Whether the callback request would have been queued successfully.", + "nullable": true, + "type": "boolean" + } + }, + "title": "Callback (validation).", + "type": "object" + }, + "schemas.call_event_subscription.CallEventSubscriptionCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of call event subscriptions.", + "items": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of call event subscriptions.", + "type": "object" + }, + "schemas.call_event_subscription.CallEventSubscriptionProto": { + "properties": { + "call_states": { + "description": "The call event subscription's list of call states.", + "items": { + "enum": [ + "admin", + "admin_recording", + "ai_playbook", + "all", + "barge", + "blocked", + "call_transcription", + "calling", + "connected", + "csat", + "dispositions", + "eavesdrop", + "hangup", + "hold", + "merged", + "missed", + "monitor", + "parked", + "pcsat", + "postcall", + "preanswer", + "queued", + "recap_action_items", + "recap_outcome", + "recap_purposes", + "recap_summary", + "recording", + "ringing", + "takeover", + "transcription", + "voicemail", + "voicemail_uploaded" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "enabled": { + "default": true, + "description": "Whether or not the call event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "group_calls_only": { + "description": "Call event subscription for group calls only.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook that's associated with this event subscription.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "title": "Call event subscription.", + "type": "object" + }, + "schemas.call_event_subscription.CreateCallEventSubscription": { + "properties": { + "call_states": { + "description": "The call event subscription's list of call states.", + "items": { + "enum": [ + "admin", + "admin_recording", + "ai_playbook", + "all", + "barge", + "blocked", + "call_transcription", + "calling", + "connected", + "csat", + "dispositions", + "eavesdrop", + "hangup", + "hold", + "merged", + "missed", + "monitor", + "parked", + "pcsat", + "postcall", + "preanswer", + "queued", + "recap_action_items", + "recap_outcome", + "recap_purposes", + "recap_summary", + "recording", + "ringing", + "takeover", + "transcription", + "voicemail", + "voicemail_uploaded" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "enabled": { + "default": true, + "description": "Whether or not the call event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_calls_only": { + "description": "Call event subscription for group calls only.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call_event_subscription.UpdateCallEventSubscription": { + "properties": { + "call_states": { + "description": "The call event subscription's list of call states.", + "items": { + "enum": [ + "admin", + "admin_recording", + "ai_playbook", + "all", + "barge", + "blocked", + "call_transcription", + "calling", + "connected", + "csat", + "dispositions", + "eavesdrop", + "hangup", + "hold", + "merged", + "missed", + "monitor", + "parked", + "pcsat", + "postcall", + "preanswer", + "queued", + "recap_action_items", + "recap_outcome", + "recap_purposes", + "recap_summary", + "recording", + "ringing", + "takeover", + "transcription", + "voicemail", + "voicemail_uploaded" + ], + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "enabled": { + "default": true, + "description": "Whether or not the call event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_calls_only": { + "description": "Call event subscription for group calls only.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.call_label.CompanyCallLabels": { + "properties": { + "labels": { + "description": "The labels associated to this company.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Company Labels.", + "type": "object" + }, + "schemas.call_review_share_link.CallReviewShareLink": { + "properties": { + "access_link": { + "description": "The access link where the call review can be listened or downloaded.", + "nullable": true, + "type": "string" + }, + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "id": { + "description": "The call review share link's ID.", + "nullable": true, + "type": "string" + }, + "privacy": { + "description": "The privacy state of the call review sharel link.", + "enum": [ + "company", + "public" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Reponse for the call review share link.", + "type": "object" + }, + "schemas.call_review_share_link.CreateCallReviewShareLink": { + "properties": { + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "privacy": { + "default": "company", + "description": "The privacy state of the recording share link, 'company' will be set as default.", + "enum": [ + "company", + "public" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Input for POST request to create a call review share link.", + "type": "object" + }, + "schemas.call_review_share_link.UpdateCallReviewShareLink": { + "properties": { + "privacy": { + "description": "The privacy state of the recording share link", + "enum": [ + "company", + "public" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "privacy" + ], + "title": "Input for PUT request to update a call review share link.", + "type": "object" + }, + "schemas.call_router.ApiCallRouterCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of call routers.", + "items": { + "$ref": "#/components/schemas/schemas.call_router.ApiCallRouterProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of API call routers.", + "type": "object" + }, + "schemas.call_router.ApiCallRouterProto": { + "properties": { + "default_target_id": { + "description": "The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "default_target_type": { + "description": "The entity type of the default target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "description": "If set to False, the call router will skip the routing url and instead forward calls straight to the default target.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The API call router's ID.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the router.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the office to which this router belongs.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "The phone numbers that will cause inbound calls to hit this call router.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "routing_url": { + "description": "The URL that should be used to drive call routing decisions.", + "nullable": true, + "type": "string" + }, + "signature": { + "$ref": "#/components/schemas/schemas.signature.SignatureProto", + "description": "The signature that will be used to sign JWTs for routing requests.", + "nullable": true, + "type": "object" + } + }, + "title": "API call router.", + "type": "object" + }, + "schemas.call_router.CreateApiCallRouterMessage": { + "properties": { + "default_target_id": { + "description": "The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "default_target_type": { + "description": "The entity type of the default target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "description": "If set to False, the call router will skip the routing url and instead forward calls straight to the default target.", + "nullable": true, + "type": "boolean" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the router.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the office to which this router belongs.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_url": { + "description": "The URL that should be used to drive call routing decisions.", + "nullable": true, + "type": "string" + }, + "secret": { + "description": "[single-line only]\n\nThe call router's signature secret. This is a plain text string that you should generate with a minimum length of 32 characters.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "default_target_id", + "default_target_type", + "name", + "office_id", + "routing_url" + ], + "type": "object" + }, + "schemas.call_router.UpdateApiCallRouterMessage": { + "properties": { + "default_target_id": { + "description": "The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "default_target_type": { + "description": "The entity type of the default target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "description": "If set to False, the call router will skip the routing url and instead forward calls straight to the default target.", + "nullable": true, + "type": "boolean" + }, + "name": { + "description": "[single-line only]\n\nA human-readable display name for the router.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the office to which this router belongs.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "reset_error_count": { + "description": "Sets the auto-disablement routing error count back to zero.\n\nCall routers maintain a count of the number of errors that have occured within the past hour, and automatically become disabled when that count exceeds 10.\n\nSetting enabled to true via the API will not reset that count, which means that the router will likely become disabled again after one more error. In most cases, this will be useful for testing fixes in your routing API, but in some circumstances it may be desirable to reset that counter.", + "nullable": true, + "type": "boolean" + }, + "routing_url": { + "description": "The URL that should be used to drive call routing decisions.", + "nullable": true, + "type": "string" + }, + "secret": { + "description": "[single-line only]\n\nThe call router's signature secret. This is a plain text string that you should generate with a minimum length of 32 characters.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.caller_id.CallerIdProto": { + "properties": { + "caller_id": { + "description": "The caller id number for the user", + "nullable": true, + "type": "string" + }, + "forwarding_numbers": { + "description": "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "groups": { + "description": "The groups from the user", + "items": { + "$ref": "#/components/schemas/schemas.caller_id.GroupProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "id": { + "description": "The ID of the user.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "office_main_line": { + "description": "The office main line number", + "nullable": true, + "type": "string" + }, + "phone_numbers": { + "description": "A list of phone numbers belonging to this user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "primary_phone": { + "description": "The user primary phone number", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id" + ], + "title": "Caller ID.", + "type": "object" + }, + "schemas.caller_id.GroupProto": { + "properties": { + "caller_id": { + "description": "A caller id from the operator group. (e164-formatted)", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "[single-line only]\n\nThe operator group display name", + "nullable": true, + "type": "string" + } + }, + "title": "Group caller ID.", + "type": "object" + }, + "schemas.caller_id.SetCallerIdMessage": { + "properties": { + "caller_id": { + "description": "Phone number (e164 formatted) that will be defined as a Caller ID for the target. Use 'blocked' to block the Caller ID.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "caller_id" + ], + "type": "object" + }, + "schemas.change_log_event_subscription.ChangeLogEventSubscriptionCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of change log event subscriptions.", + "items": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of change log event subscriptions.", + "type": "object" + }, + "schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto": { + "properties": { + "enabled": { + "default": true, + "description": "Whether or not the change log event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "title": "Change log event subscription.", + "type": "object" + }, + "schemas.change_log_event_subscription.CreateChangeLogEventSubscription": { + "properties": { + "enabled": { + "default": true, + "description": "Whether or not the this change log event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.change_log_event_subscription.UpdateChangeLogEventSubscription": { + "properties": { + "enabled": { + "default": true, + "description": "Whether or not the change log event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.channel.ChannelCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of channels.", + "items": { + "$ref": "#/components/schemas/schemas.channel.ChannelProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of channels.", + "type": "object" + }, + "schemas.channel.ChannelProto": { + "properties": { + "id": { + "description": "The channel id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nThe channel name.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "name" + ], + "title": "Channel.", + "type": "object" + }, + "schemas.channel.CreateChannelMessage": { + "properties": { + "description": { + "description": "The description of the channel.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nThe name of the channel.", + "nullable": true, + "type": "string" + }, + "privacy_type": { + "description": "The privacy type of the channel.", + "enum": [ + "private", + "public" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The ID of the user who owns the channel. Just for company level API key.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "description", + "name", + "privacy_type" + ], + "type": "object" + }, + "schemas.coaching_team.CoachingTeamCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of coaching teams.", + "items": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of coaching team.", + "type": "object" + }, + "schemas.coaching_team.CoachingTeamMemberCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of team members.", + "items": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamMemberProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of coaching team members.", + "type": "object" + }, + "schemas.coaching_team.CoachingTeamMemberMessage": { + "properties": { + "member_id": { + "description": "The id of the user added to the coaching team.", + "nullable": true, + "type": "string" + }, + "role": { + "description": "The role of the user added.", + "enum": [ + "coach", + "trainee" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "member_id", + "role" + ], + "title": "Coaching team membership.", + "type": "object" + }, + "schemas.coaching_team.CoachingTeamMemberProto": { + "properties": { + "admin_office_ids": { + "description": "The list of ids of offices where the user is an admin.", + "items": { + "format": "int64", + "type": "integer" + }, + "nullable": true, + "type": "array" + }, + "company_id": { + "description": "The id of user's company.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "country": { + "description": "Country of the user.", + "nullable": true, + "type": "string" + }, + "date_active": { + "description": "The date when the user is activated.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_added": { + "description": "The date when the user is added.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_first_login": { + "description": "The date when the user is logged in first time.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "do_not_disturb": { + "description": "Boolean to tell if the user is on DND.", + "nullable": true, + "type": "boolean" + }, + "emails": { + "description": "Emails of the user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "Extension of the user.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nFirst name of the user.", + "nullable": true, + "type": "string" + }, + "forwarding_numbers": { + "description": "The list of forwarding numbers set for the user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "id": { + "description": "Unique id of the user.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "image_url": { + "description": "Link to the user's profile image.", + "nullable": true, + "type": "string" + }, + "is_admin": { + "description": "Boolean to tell if the user is admin.", + "nullable": true, + "type": "boolean" + }, + "is_available": { + "description": "Boolean to tell if the user is available.", + "nullable": true, + "type": "boolean" + }, + "is_on_duty": { + "description": "Boolean to tell if the user is on duty.", + "nullable": true, + "type": "boolean" + }, + "is_online": { + "description": "Boolean to tell if the user is online.", + "nullable": true, + "type": "boolean" + }, + "is_super_admin": { + "description": "Boolean to tell if the user is super admin.", + "nullable": true, + "type": "boolean" + }, + "job_title": { + "description": "[single-line only]\n\nJob title of the user.", + "nullable": true, + "type": "string" + }, + "language": { + "description": "Language of the user.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nLast name of the User.", + "nullable": true, + "type": "string" + }, + "license": { + "description": "License of the user.", + "enum": [ + "admins", + "agents", + "dpde_all", + "dpde_one", + "lite_lines", + "lite_support_agents", + "magenta_lines", + "talk" + ], + "nullable": true, + "type": "string" + }, + "location": { + "description": "[single-line only]\n\nThe self-reported location of the user.", + "nullable": true, + "type": "string" + }, + "muted": { + "description": "Boolean to tell if the user is muted.", + "nullable": true, + "type": "boolean" + }, + "office_id": { + "description": "Id of the user's office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "The list of phone numbers assigned to the user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "role": { + "description": "The role of the user within the coaching team.", + "enum": [ + "coach", + "trainee" + ], + "nullable": true, + "type": "string" + }, + "state": { + "description": "The enablement state of the user.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "status_message": { + "description": "[single-line only]\n\nStatus message set by the user.", + "nullable": true, + "type": "string" + }, + "timezone": { + "description": "Timezone of the user.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "id", + "role" + ], + "title": "Coaching team member.", + "type": "object" + }, + "schemas.coaching_team.CoachingTeamProto": { + "properties": { + "allow_trainee_eavesdrop": { + "description": "The boolean to tell if trainees are allowed to eavesdrop.", + "nullable": true, + "type": "boolean" + }, + "company_id": { + "description": "The company's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "country": { + "description": "The country in which the coaching team is situated.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Id of the coaching team.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nName of the coaching team.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The office's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "The phone number(s) assigned to this coaching team.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "ring_seconds": { + "description": "The number of seconds to ring the main line before going to voicemail.\n\n(or an other-wise-specified no_operators_action).", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "state": { + "description": "The enablement state of the team.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "team_description": { + "description": "Description of the coaching team.", + "nullable": true, + "type": "string" + } + }, + "title": "Coaching team.", + "type": "object" + }, + "schemas.company.CompanyProto": { + "properties": { + "account_type": { + "description": "Company pricing tier.", + "enum": [ + "enterprise", + "free", + "pro", + "standard" + ], + "nullable": true, + "type": "string" + }, + "admin_email": { + "description": "Email address of the company administrator.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Primary country of the company.", + "nullable": true, + "type": "string" + }, + "domain": { + "description": "[single-line only]\n\nDomain name of user emails.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The company's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nThe name of the company.", + "nullable": true, + "type": "string" + }, + "office_count": { + "description": "The number of offices belonging to this company", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "state": { + "description": "Enablement state of the company.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Company.", + "type": "object" + }, + "schemas.contact.ContactCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of contact objects.", + "items": { + "$ref": "#/components/schemas/schemas.contact.ContactProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of contacts.", + "type": "object" + }, + "schemas.contact.ContactProto": { + "properties": { + "company_name": { + "description": "[single-line only]\n\nThe name of the company that this contact is employed by.", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "[single-line only]\n\nThe formatted name that will be displayed for this contact.", + "nullable": true, + "type": "string" + }, + "emails": { + "description": "The email addresses associated with this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The contact's extension number.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe given name of the contact.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the contact.", + "nullable": true, + "type": "string" + }, + "job_title": { + "description": "[single-line only]\n\nThe job title of this contact.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe family name of the contact.", + "nullable": true, + "type": "string" + }, + "owner_id": { + "description": "The ID of the entity that owns this contact.", + "nullable": true, + "type": "string" + }, + "phones": { + "description": "The phone numbers associated with this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "primary_email": { + "description": "The email address to display in a context where only one email can be shown.", + "nullable": true, + "type": "string" + }, + "primary_phone": { + "description": "The primary phone number to be used when calling this contact.", + "nullable": true, + "type": "string" + }, + "trunk_group": { + "description": "[Deprecated]", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Either shared or local.", + "enum": [ + "local", + "shared" + ], + "nullable": true, + "type": "string" + }, + "urls": { + "description": "A list of websites associated with or belonging to this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Contact.", + "type": "object" + }, + "schemas.contact.CreateContactMessage": { + "properties": { + "company_name": { + "description": "[single-line only]\n\nThe contact's company name.", + "nullable": true, + "type": "string" + }, + "emails": { + "description": "The contact's emails.\n\nThe first email in the list is the contact's primary email.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The contact's extension number.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe contact's first name.", + "nullable": true, + "type": "string" + }, + "job_title": { + "description": "[single-line only]\n\nThe contact's job title.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe contact's last name.", + "nullable": true, + "type": "string" + }, + "owner_id": { + "description": "The id of the user who will own this contact.\n\nIf provided, a local contact will be created for this user. Otherwise, the contact will be created as a shared contact in your company.", + "nullable": true, + "type": "string" + }, + "phones": { + "description": "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "trunk_group": { + "description": "[Deprecated]", + "nullable": true, + "type": "string" + }, + "urls": { + "description": "A list of websites associated with or belonging to this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "first_name", + "last_name" + ], + "type": "object" + }, + "schemas.contact.CreateContactMessageWithUid": { + "properties": { + "company_name": { + "description": "[single-line only]\n\nThe contact's company name.", + "nullable": true, + "type": "string" + }, + "emails": { + "description": "The contact's emails.\n\nThe first email in the list is the contact's primary email.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The contact's extension number.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe contact's first name.", + "nullable": true, + "type": "string" + }, + "job_title": { + "description": "[single-line only]\n\nThe contact's job title.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe contact's last name.", + "nullable": true, + "type": "string" + }, + "phones": { + "description": "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "trunk_group": { + "description": "[Deprecated]", + "nullable": true, + "type": "string" + }, + "uid": { + "description": "The unique id to be included as part of the contact's generated id.", + "nullable": true, + "type": "string" + }, + "urls": { + "description": "A list of websites associated with or belonging to this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "first_name", + "last_name", + "uid" + ], + "type": "object" + }, + "schemas.contact.UpdateContactMessage": { + "properties": { + "company_name": { + "description": "[single-line only]\n\nThe contact's company name.", + "nullable": true, + "type": "string" + }, + "emails": { + "description": "The contact's emails.\n\nThe first email in the list is the contact's primary email.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The contact's extension number.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe contact's first name.", + "nullable": true, + "type": "string" + }, + "job_title": { + "description": "[single-line only]\n\nThe contact's job title.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe contact's last name.", + "nullable": true, + "type": "string" + }, + "phones": { + "description": "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "trunk_group": { + "description": "[Deprecated]", + "nullable": true, + "type": "string" + }, + "urls": { + "description": "A list of websites associated with or belonging to this contact.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.contact_event_subscription.ContactEventSubscriptionCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list event subscriptions.", + "items": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of contact event subscriptions.", + "type": "object" + }, + "schemas.contact_event_subscription.ContactEventSubscriptionProto": { + "properties": { + "contact_type": { + "description": "The contact type this event subscription subscribes to.", + "enum": [ + "local", + "shared" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the contact event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The ID of the contact event subscription object.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "title": "Contact event subscription.", + "type": "object" + }, + "schemas.contact_event_subscription.CreateContactEventSubscription": { + "properties": { + "contact_type": { + "description": "The contact type this event subscription subscribes to.", + "enum": [ + "local", + "shared" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the contact event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "contact_type" + ], + "type": "object" + }, + "schemas.contact_event_subscription.UpdateContactEventSubscription": { + "properties": { + "contact_type": { + "description": "The contact type this event subscription subscribes to.", + "enum": [ + "local", + "shared" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the contact event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "contact_type" + ], + "type": "object" + }, + "schemas.custom_ivr.CreateCustomIvrMessage": { + "properties": { + "description": { + "description": "[single-line only]\n\nThe description of the new IVR. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "file": { + "description": "An MP3 audio file. The file needs to be Base64-encoded.", + "format": "byte", + "nullable": true, + "type": "string" + }, + "ivr_type": { + "description": "Type of IVR.", + "enum": [ + "ASK_FIRST_OPERATOR_NOT_AVAILABLE", + "AUTO_RECORDING", + "CALLAI_AUTO_RECORDING", + "CG_AUTO_RECORDING", + "CLOSED", + "CLOSED_DEPARTMENT_INTRO", + "CLOSED_MENU", + "CLOSED_MENU_OPTION", + "CSAT_INTRO", + "CSAT_OUTRO", + "CSAT_PREAMBLE", + "CSAT_QUESTION", + "DEPARTMENT_INTRO", + "GREETING", + "HOLD_AGENT_READY", + "HOLD_APPREC", + "HOLD_CALLBACK_ACCEPT", + "HOLD_CALLBACK_ACCEPTED", + "HOLD_CALLBACK_CONFIRM", + "HOLD_CALLBACK_CONFIRM_NUMBER", + "HOLD_CALLBACK_DIFFERENT_NUMBER", + "HOLD_CALLBACK_DIRECT", + "HOLD_CALLBACK_FULFILLED", + "HOLD_CALLBACK_INVALID_NUMBER", + "HOLD_CALLBACK_KEYPAD", + "HOLD_CALLBACK_REJECT", + "HOLD_CALLBACK_REJECTED", + "HOLD_CALLBACK_REQUEST", + "HOLD_CALLBACK_REQUESTED", + "HOLD_CALLBACK_SAME_NUMBER", + "HOLD_CALLBACK_TRY_AGAIN", + "HOLD_CALLBACK_UNDIALABLE", + "HOLD_ESCAPE_VM_EIGHT", + "HOLD_ESCAPE_VM_FIVE", + "HOLD_ESCAPE_VM_FOUR", + "HOLD_ESCAPE_VM_NINE", + "HOLD_ESCAPE_VM_ONE", + "HOLD_ESCAPE_VM_POUND", + "HOLD_ESCAPE_VM_SEVEN", + "HOLD_ESCAPE_VM_SIX", + "HOLD_ESCAPE_VM_STAR", + "HOLD_ESCAPE_VM_TEN", + "HOLD_ESCAPE_VM_THREE", + "HOLD_ESCAPE_VM_TWO", + "HOLD_ESCAPE_VM_ZERO", + "HOLD_INTERRUPT", + "HOLD_INTRO", + "HOLD_MUSIC", + "HOLD_POSITION_EIGHT", + "HOLD_POSITION_FIVE", + "HOLD_POSITION_FOUR", + "HOLD_POSITION_MORE", + "HOLD_POSITION_NINE", + "HOLD_POSITION_ONE", + "HOLD_POSITION_SEVEN", + "HOLD_POSITION_SIX", + "HOLD_POSITION_TEN", + "HOLD_POSITION_THREE", + "HOLD_POSITION_TWO", + "HOLD_POSITION_ZERO", + "HOLD_WAIT", + "MENU", + "MENU_OPTION", + "NEXT_TARGET", + "VM_DROP_MESSAGE", + "VM_UNAVAILABLE", + "VM_UNAVAILABLE_CLOSED" + ], + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nThe name of the new IVR. Max 100 characters.", + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The ID of the target to which you want to assign this IVR.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target to which you want to assign this IVR.", + "enum": [ + "callcenter", + "coachingteam", + "department", + "office", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "file", + "ivr_type", + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.custom_ivr.CustomIvrCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of IVRs.", + "items": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of Custom IVRs.", + "type": "object" + }, + "schemas.custom_ivr.CustomIvrDetailsProto": { + "properties": { + "date_added": { + "description": "Date when this IVR was added.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "description": { + "description": "[single-line only]\n\nThe description of the IVR.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "Id of this IVR.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nThe name of this IVR.", + "nullable": true, + "type": "string" + }, + "selected": { + "description": "True if this IVR is selected for this type of IVR.", + "nullable": true, + "type": "boolean" + }, + "text": { + "description": "The text for this IVR if there is no mp3.", + "nullable": true, + "type": "string" + } + }, + "title": "Custom IVR details.", + "type": "object" + }, + "schemas.custom_ivr.CustomIvrProto": { + "properties": { + "ivr_type": { + "description": "Type of IVR.", + "enum": [ + "ASK_FIRST_OPERATOR_NOT_AVAILABLE", + "AUTO_RECORDING", + "CALLAI_AUTO_RECORDING", + "CG_AUTO_RECORDING", + "CLOSED", + "CLOSED_DEPARTMENT_INTRO", + "CLOSED_MENU", + "CLOSED_MENU_OPTION", + "CSAT_INTRO", + "CSAT_OUTRO", + "CSAT_PREAMBLE", + "CSAT_QUESTION", + "DEPARTMENT_INTRO", + "GREETING", + "HOLD_AGENT_READY", + "HOLD_APPREC", + "HOLD_CALLBACK_ACCEPT", + "HOLD_CALLBACK_ACCEPTED", + "HOLD_CALLBACK_CONFIRM", + "HOLD_CALLBACK_CONFIRM_NUMBER", + "HOLD_CALLBACK_DIFFERENT_NUMBER", + "HOLD_CALLBACK_DIRECT", + "HOLD_CALLBACK_FULFILLED", + "HOLD_CALLBACK_INVALID_NUMBER", + "HOLD_CALLBACK_KEYPAD", + "HOLD_CALLBACK_REJECT", + "HOLD_CALLBACK_REJECTED", + "HOLD_CALLBACK_REQUEST", + "HOLD_CALLBACK_REQUESTED", + "HOLD_CALLBACK_SAME_NUMBER", + "HOLD_CALLBACK_TRY_AGAIN", + "HOLD_CALLBACK_UNDIALABLE", + "HOLD_ESCAPE_VM_EIGHT", + "HOLD_ESCAPE_VM_FIVE", + "HOLD_ESCAPE_VM_FOUR", + "HOLD_ESCAPE_VM_NINE", + "HOLD_ESCAPE_VM_ONE", + "HOLD_ESCAPE_VM_POUND", + "HOLD_ESCAPE_VM_SEVEN", + "HOLD_ESCAPE_VM_SIX", + "HOLD_ESCAPE_VM_STAR", + "HOLD_ESCAPE_VM_TEN", + "HOLD_ESCAPE_VM_THREE", + "HOLD_ESCAPE_VM_TWO", + "HOLD_ESCAPE_VM_ZERO", + "HOLD_INTERRUPT", + "HOLD_INTRO", + "HOLD_MUSIC", + "HOLD_POSITION_EIGHT", + "HOLD_POSITION_FIVE", + "HOLD_POSITION_FOUR", + "HOLD_POSITION_MORE", + "HOLD_POSITION_NINE", + "HOLD_POSITION_ONE", + "HOLD_POSITION_SEVEN", + "HOLD_POSITION_SIX", + "HOLD_POSITION_TEN", + "HOLD_POSITION_THREE", + "HOLD_POSITION_TWO", + "HOLD_POSITION_ZERO", + "HOLD_WAIT", + "MENU", + "MENU_OPTION", + "NEXT_TARGET", + "VM_DROP_MESSAGE", + "VM_UNAVAILABLE", + "VM_UNAVAILABLE_CLOSED" + ], + "nullable": true, + "type": "string" + }, + "ivrs": { + "description": "A list of IVR detail objects.", + "items": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrDetailsProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Custom IVR.", + "type": "object" + }, + "schemas.custom_ivr.UpdateCustomIvrDetailsMessage": { + "properties": { + "description": { + "description": "[single-line only]\n\nThe description of the IVR.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nThe name of this IVR.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.custom_ivr.UpdateCustomIvrMessage": { + "properties": { + "ivr_id": { + "description": "The id of the ivr that you want to use for the ivr type.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "select_option": { + "description": "For call center auto call recording only. Set ivr for inbound or outbound. Default is both.", + "enum": [ + "inbound", + "outbound" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "ivr_id" + ], + "type": "object" + }, + "schemas.deskphone.DeskPhone": { + "properties": { + "byod": { + "description": "Boolean indicating whether this desk phone was purchased through Dialpad.", + "nullable": true, + "type": "boolean" + }, + "device_model": { + "description": "[single-line only]\n\nThe model name of the device.", + "nullable": true, + "type": "string" + }, + "firmware_version": { + "description": "[single-line only]\n\nThe firmware version currently loaded onto the device.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the desk phone.", + "nullable": true, + "type": "string" + }, + "mac_address": { + "description": "[single-line only]\n\nThe MAC address of the device.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nA user-prescibed name for this device.", + "nullable": true, + "type": "string" + }, + "owner_id": { + "description": "The ID of the device owner.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "owner_type": { + "description": "The entity type of the device owner.", + "enum": [ + "room", + "user" + ], + "nullable": true, + "type": "string" + }, + "password": { + "description": "[single-line only]\n\nA password required to make calls on with the device.", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The phone number associated with this device.", + "nullable": true, + "type": "string" + }, + "port": { + "description": "The SIP port number.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "realm": { + "description": "The SIP realm that this device should use.", + "nullable": true, + "type": "string" + }, + "ring_notification": { + "description": "A boolean indicating whether this device should ring when the user receives a call.", + "nullable": true, + "type": "boolean" + }, + "sip_transport_type": { + "description": "The SIP transport layer protocol.", + "enum": [ + "tls" + ], + "nullable": true, + "type": "string" + }, + "type": { + "description": "User phone, or room phone.", + "enum": [ + "ata", + "audiocodes", + "c2t", + "ciscompp", + "dect", + "grandstream", + "mini", + "mitel", + "obi", + "polyandroid", + "polycom", + "sip", + "tickiot", + "yealink" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Desk phone.", + "type": "object" + }, + "schemas.deskphone.DeskPhoneCollection": { + "properties": { + "items": { + "description": "A list of desk phones.", + "items": { + "$ref": "#/components/schemas/schemas.deskphone.DeskPhone", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of desk phones.", + "type": "object" + }, + "schemas.e164_format.FormatNumberResponse": { + "properties": { + "area_code": { + "description": "First portion of local formatted number. e.g. \"(555)\"", + "nullable": true, + "type": "string" + }, + "country_code": { + "description": "Abbreviated country name in ISO 3166-1 alpha-2 format. e.g. \"US\"", + "nullable": true, + "type": "string" + }, + "e164_number": { + "description": "Number in local format.\n\ne.g. \"(555) 555-5555\"", + "nullable": true, + "type": "string" + }, + "local_number": { + "description": "Number in E.164 format. e.g. \"+15555555555\"", + "nullable": true, + "type": "string" + } + }, + "title": "Formatted number.", + "type": "object" + }, + "schemas.faxline.CreateFaxNumberMessage": { + "properties": { + "line": { + "description": "Line to assign.", + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/schemas.faxline.ReservedLineType" + }, + { + "$ref": "#/components/schemas/schemas.faxline.SearchLineType" + }, + { + "$ref": "#/components/schemas/schemas.faxline.TollfreeLineType" + } + ] + }, + "target": { + "$ref": "#/components/schemas/schemas.faxline.Target", + "description": "The target to assign the number to.", + "nullable": true, + "type": "object" + } + }, + "required": [ + "line", + "target" + ], + "type": "object" + }, + "schemas.faxline.FaxNumberProto": { + "properties": { + "area_code": { + "description": "The area code of the number.", + "nullable": true, + "type": "string" + }, + "company_id": { + "description": "The ID of the associated company.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "number": { + "description": "A mock parameter for testing.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the associate office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_id": { + "description": "The ID of the target to which this number is assigned.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target to which this number is assigned.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "number" + ], + "title": "Fax number details.", + "type": "object" + }, + "schemas.faxline.ReservedLineType": { + "properties": { + "number": { + "description": "A phone number to assign. (e164-formatted)", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Type of line.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "number", + "type" + ], + "title": "Reserved number fax line assignment.", + "type": "object" + }, + "schemas.faxline.SearchLineType": { + "properties": { + "area_code": { + "description": "An area code in which to find an available phone number for assignment. If there is no area code provided, office's area code will be used.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Type of line.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "area_code", + "type" + ], + "title": "Search fax line assignment.", + "type": "object" + }, + "schemas.faxline.Target": { + "properties": { + "target_id": { + "description": "The ID of the target to assign the fax line to.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Type of the target to assign the fax line to.", + "enum": [ + "department", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.faxline.TollfreeLineType": { + "properties": { + "type": { + "description": "Type of line.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "Tollfree fax line assignment.", + "type": "object" + }, + "schemas.group.AddCallCenterOperatorMessage": { + "properties": { + "keep_paid_numbers": { + "default": true, + "description": "Whether or not to keep phone numbers when switching to a support license.\n\nNote: Phone numbers require additional number licenses under a support license.", + "nullable": true, + "type": "boolean" + }, + "license_type": { + "default": "agents", + "description": "The type of license to assign to the new operator if a license is required.\n(`agents` or `lite_support_agents`). Defaults to `agents`", + "enum": [ + "agents", + "lite_support_agents" + ], + "nullable": true, + "type": "string" + }, + "role": { + "default": "operator", + "description": "The role the user should assume.", + "enum": [ + "admin", + "operator", + "supervisor" + ], + "nullable": true, + "type": "string" + }, + "skill_level": { + "default": 100, + "description": "Skill level of the operator. Integer value in range 1 - 100. Default 100.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "user_id": { + "description": "The ID of the user.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "type": "object" + }, + "schemas.group.AddOperatorMessage": { + "properties": { + "operator_id": { + "description": "ID of the operator to add.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "operator_type": { + "description": "Type of the operator to add. (`user` or `room`)", + "enum": [ + "room", + "user" + ], + "nullable": true, + "type": "string" + }, + "role": { + "default": "operator", + "description": "The role of the new operator. (`operator` or `admin`)", + "enum": [ + "admin", + "operator" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "operator_id", + "operator_type" + ], + "type": "object" + }, + "schemas.group.AdvancedSettings": { + "properties": { + "auto_call_recording": { + "$ref": "#/components/schemas/schemas.group.AutoCallRecording", + "description": "Choose which calls to and from this call center get automatically recorded. Recordings are only available to administrators of this call center, which can be found in the Dialpad app and the Calls List.", + "nullable": true, + "type": "object" + }, + "max_wrap_up_seconds": { + "description": "Include a post-call wrap-up time before agents can receive their next call. Default is 0.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.group.Alerts": { + "properties": { + "cc_service_level": { + "description": "Alert supervisors when the service level drops below how many percent. Default is 95%.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "cc_service_level_seconds": { + "description": "Inbound calls should be answered within how many seconds. Default is 60.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.group.AutoCallRecording": { + "properties": { + "allow_pause_recording": { + "description": "Allow agents to stop/restart a recording during a call. Default is False.", + "nullable": true, + "type": "boolean" + }, + "call_recording_inbound": { + "description": "Whether or not inbound calls to this call center get automatically recorded. Default is False.", + "nullable": true, + "type": "boolean" + }, + "call_recording_outbound": { + "description": "Whether or not outbound calls from this call center get automatically recorded. Default is False.", + "nullable": true, + "type": "boolean" + } + }, + "type": "object" + }, + "schemas.group.AvailabilityStatusProto": { + "properties": { + "name": { + "description": "[single-line only]\n\nA descriptive name for the status. If the Call Center is within any holiday, it displays it.", + "nullable": true, + "type": "string" + }, + "status": { + "description": "Status of this Call Center. It can be open, closed, holiday_open or holiday_closed", + "nullable": true, + "type": "string" + } + }, + "required": [ + "status" + ], + "title": "Availability Status for a Call Center.", + "type": "object" + }, + "schemas.group.CallCenterCollection": { + "properties": { + "cursor": { + "description": "A cursor string that can be used to fetch the subsequent page.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list containing the first page of results.", + "items": { + "$ref": "#/components/schemas/schemas.group.CallCenterProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of call centers.", + "type": "object" + }, + "schemas.group.CallCenterProto": { + "properties": { + "advanced_settings": { + "$ref": "#/components/schemas/schemas.group.AdvancedSettings", + "description": "Configure call center advanced settings.", + "nullable": true, + "type": "object" + }, + "alerts": { + "$ref": "#/components/schemas/schemas.group.Alerts", + "description": "Set when alerts will be triggered.", + "nullable": true, + "type": "object" + }, + "availability_status": { + "description": "Availability status of the group.", + "enum": [ + "closed", + "holiday_closed", + "holiday_open", + "open" + ], + "nullable": true, + "type": "string" + }, + "country": { + "description": "The country in which the user group resides.", + "nullable": true, + "type": "string" + }, + "first_action": { + "description": "The initial action to take upon receiving a new call.", + "enum": [ + "menu", + "operators" + ], + "nullable": true, + "type": "string" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the call center.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueCallCenter", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The ID of the group entity.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the group.", + "nullable": true, + "type": "string" + }, + "no_operators_action": { + "description": "The action to take if there are no operators available to accept an inbound call.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the office in which this group resides.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "A list of phone numbers belonging to this group.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The current enablement state of this group.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "timezone": { + "description": "The timezone of the group.", + "nullable": true, + "type": "string" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Call center.", + "type": "object" + }, + "schemas.group.CallCenterStatusProto": { + "properties": { + "availability": { + "$ref": "#/components/schemas/schemas.group.AvailabilityStatusProto", + "description": "Availability of the Call Center.", + "nullable": true, + "type": "object" + }, + "capacity": { + "description": "The number of available operators.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "longest_call_wait_time": { + "description": "The longest queued call, in seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "on_duty_operators": { + "description": "The amount of operators On Duty", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "pending": { + "description": "The number of on-hold calls.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "availability", + "capacity", + "longest_call_wait_time", + "on_duty_operators", + "pending" + ], + "title": "Status information for a Call Center.", + "type": "object" + }, + "schemas.group.CreateCallCenterMessage": { + "properties": { + "advanced_settings": { + "$ref": "#/components/schemas/schemas.group.AdvancedSettings", + "description": "Configure advanced call center settings.", + "nullable": true, + "type": "object" + }, + "alerts": { + "$ref": "#/components/schemas/schemas.group.Alerts", + "description": "Set when alerts will be triggered.", + "nullable": true, + "type": "object" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the call center. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueCallCenter", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the call center. Max 100 characters.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The id of the office to which the call center belongs..", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "name", + "office_id" + ], + "type": "object" + }, + "schemas.group.CreateDepartmentMessage": { + "properties": { + "auto_call_recording": { + "description": "Whether or not automatically record all calls of this department. Default is False.", + "nullable": true, + "type": "boolean" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the department. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueDepartment", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the department wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the department. Max 100 characters.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The id of the office to which the department belongs..", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "name", + "office_id" + ], + "type": "object" + }, + "schemas.group.DepartmentCollection": { + "properties": { + "cursor": { + "description": "A cursor string that can be used to fetch the subsequent page.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list containing the first page of results.", + "items": { + "$ref": "#/components/schemas/schemas.group.DepartmentProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of departments.", + "type": "object" + }, + "schemas.group.DepartmentProto": { + "properties": { + "auto_call_recording": { + "description": "Whether or not automatically record all calls of this department. Default is False.", + "nullable": true, + "type": "boolean" + }, + "availability_status": { + "description": "Availability status of the group.", + "enum": [ + "closed", + "holiday_closed", + "holiday_open", + "open" + ], + "nullable": true, + "type": "string" + }, + "country": { + "description": "The country in which the user group resides.", + "nullable": true, + "type": "string" + }, + "first_action": { + "description": "The initial action to take upon receiving a new call.", + "enum": [ + "menu", + "operators" + ], + "nullable": true, + "type": "string" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the call center.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueDepartment", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The ID of the group entity.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the group.", + "nullable": true, + "type": "string" + }, + "no_operators_action": { + "description": "The action to take if there are no operators available to accept an inbound call.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the office in which this group resides.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "A list of phone numbers belonging to this group.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The current enablement state of this group.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "timezone": { + "description": "The timezone of the group.", + "nullable": true, + "type": "string" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"]", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Department.", + "type": "object" + }, + "schemas.group.DtmfMapping": { + "properties": { + "input": { + "description": "The DTMF key associated with this menu item. (0-9)", + "nullable": true, + "type": "string" + }, + "options": { + "$ref": "#/components/schemas/schemas.group.DtmfOptions", + "description": "The action that should be taken if the input key is pressed.", + "nullable": true, + "type": "object" + } + }, + "type": "object" + }, + "schemas.group.DtmfOptions": { + "properties": { + "action": { + "description": "The routing action type.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "action_target_id": { + "description": "The ID of the target that should be dialed.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "action_target_type": { + "description": "The type of the target that should be dialed.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "contact", + "contactgroup", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "title": "DTMF routing options.", + "type": "object" + }, + "schemas.group.HoldQueueCallCenter": { + "properties": { + "allow_queue_callback": { + "description": "Whether or not to allow callers to request a callback. Default is False.", + "nullable": true, + "type": "boolean" + }, + "announce_position": { + "description": "Whether or not to let callers know their place in the queue. This option is not available when a maximum queue wait time of less than 2 minutes is selected. Default is True.", + "nullable": true, + "type": "boolean" + }, + "announcement_interval_seconds": { + "description": "Hold announcement interval wait time. Default is 2 min.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "max_hold_count": { + "description": "If all operators are busy on other calls, send callers to a hold queue. This is to specify your queue size. Choose from 1-1000. Default is 50.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "max_hold_seconds": { + "description": "Maximum queue wait time in seconds. Choose from 30s to 18000s (3 hours). Default is 900s (15 min).", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "queue_callback_dtmf": { + "description": "Allow callers to request a callback when the queue has more than queue_callback_threshold number of calls by pressing one of the followings: [0,1,2,3,4,5,6,7,8,9,*,#]. Default is 9.", + "nullable": true, + "type": "string" + }, + "queue_callback_threshold": { + "description": "Allow callers to request a callback when the queue has more than this number of calls. Default is 5.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "queue_escape_dtmf": { + "description": "Allow callers to exit the hold queue to voicemail by pressing one of the followings:\n[0,1,2,3,4,5,6,7,8,9,*,#]. Default is *.", + "nullable": true, + "type": "string" + }, + "stay_in_queue_after_closing": { + "description": "Whether or not to allow existing calls to stay in queue after the call center has closed. Default is False.", + "nullable": true, + "type": "boolean" + }, + "unattended_queue": { + "description": "Whether or not to allow callers to be placed in your hold queue when no agents are available. Default is False.", + "nullable": true, + "type": "boolean" + } + }, + "type": "object" + }, + "schemas.group.HoldQueueDepartment": { + "properties": { + "allow_queuing": { + "description": "Whether or not send callers to a hold queue, if all operators are busy on other calls. Default is False.", + "nullable": true, + "type": "boolean" + }, + "max_hold_count": { + "description": "If all operators are busy on other calls, send callers to a hold queue. This is to specify your queue size. Choose from 1-50. Default is 50.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "max_hold_seconds": { + "description": "Maximum queue wait time in seconds. Choose from 30s to 18000s (3 hours). Default is 900s (15 min).", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.group.OperatorCollection": { + "description": "Operators can be users or rooms.", + "properties": { + "rooms": { + "description": "A list of rooms that can currently act as operators for this group.", + "items": { + "$ref": "#/components/schemas/schemas.room.RoomProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "users": { + "description": "A list of users who are currently operators of this group.", + "items": { + "$ref": "#/components/schemas/schemas.user.UserProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of operators.", + "type": "object" + }, + "schemas.group.OperatorDutyStatusProto": { + "properties": { + "duty_status_reason": { + "description": "[single-line only]\n\nA description of this status.", + "nullable": true, + "type": "string" + }, + "duty_status_started": { + "description": "The time stamp, in UTC, when the current on duty status changed.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "on_duty": { + "description": "Whether the operator is currently on duty or off duty.", + "nullable": true, + "type": "boolean" + }, + "on_duty_started": { + "description": "The time stamp, in UTC, when this operator became available for contact center calls.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "on_duty_status": { + "description": "A description of operator's on duty status.", + "enum": [ + "available", + "busy", + "occupied", + "occupied-end", + "unavailable", + "wrapup", + "wrapup-end" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The ID of the operator.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.group.OperatorSkillLevelProto": { + "properties": { + "call_center_id": { + "description": "The call center's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "skill_level": { + "description": "New skill level of the operator.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "user_id": { + "description": "The ID of the operator.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.group.RemoveCallCenterOperatorMessage": { + "properties": { + "user_id": { + "description": "ID of the operator to remove.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "type": "object" + }, + "schemas.group.RemoveOperatorMessage": { + "properties": { + "operator_id": { + "description": "ID of the operator to remove.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "operator_type": { + "description": "Type of the operator to remove (`user` or `room`).", + "enum": [ + "room", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "operator_id", + "operator_type" + ], + "type": "object" + }, + "schemas.group.RoutingOptions": { + "properties": { + "closed": { + "$ref": "#/components/schemas/schemas.group.RoutingOptionsInner", + "description": "Routing options to use during off hours.", + "nullable": true, + "type": "object" + }, + "open": { + "$ref": "#/components/schemas/schemas.group.RoutingOptionsInner", + "description": "Routing options to use during open hours.", + "nullable": true, + "type": "object" + } + }, + "required": [ + "closed", + "open" + ], + "title": "Group routing options.", + "type": "object" + }, + "schemas.group.RoutingOptionsInner": { + "properties": { + "action": { + "description": "The action that should be taken if no operators are available.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "action_target_id": { + "description": "The ID of the Target that inbound calls should be routed to.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "action_target_type": { + "description": "The type of the Target that inbound calls should be routed to.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "contact", + "contactgroup", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "dtmf": { + "description": "DTMF menu options.", + "items": { + "$ref": "#/components/schemas/schemas.group.DtmfMapping", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "operator_routing": { + "description": "The routing strategy that should be used when dialing operators.", + "enum": [ + "fixedorder", + "longestidle", + "mostskilled", + "random", + "roundrobin", + "simultaneous" + ], + "nullable": true, + "type": "string" + }, + "try_dial_operators": { + "description": "Whether operators should be dialed on inbound calls.", + "nullable": true, + "type": "boolean" + } + }, + "required": [ + "action", + "try_dial_operators" + ], + "title": "Group routing options for open or closed states.", + "type": "object" + }, + "schemas.group.UpdateCallCenterMessage": { + "properties": { + "advanced_settings": { + "$ref": "#/components/schemas/schemas.group.AdvancedSettings", + "description": "Configure advanced call center settings.", + "nullable": true, + "type": "object" + }, + "alerts": { + "$ref": "#/components/schemas/schemas.group.Alerts", + "description": "Set when alerts will be triggered.", + "nullable": true, + "type": "object" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the call center. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueCallCenter", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the call center. Max 100 characters.", + "nullable": true, + "type": "string" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.group.UpdateDepartmentMessage": { + "properties": { + "auto_call_recording": { + "description": "Whether or not automatically record all calls of this department. Default is False.", + "nullable": true, + "type": "boolean" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the department. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "hold_queue": { + "$ref": "#/components/schemas/schemas.group.HoldQueueDepartment", + "description": "Configure how the calls are sent to a hold queue when all operators are busy on other calls.", + "nullable": true, + "type": "object" + }, + "hours_on": { + "description": "The time frame when the department wants to receive calls. Default value is false, which means the call center will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the department. Max 100 characters.", + "nullable": true, + "type": "string" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.group.UpdateOperatorDutyStatusMessage": { + "properties": { + "duty_status_reason": { + "description": "[single-line only]\n\nA description of this status.", + "nullable": true, + "type": "string" + }, + "on_duty": { + "description": "True if this status message indicates an \"on-duty\" status.", + "nullable": true, + "type": "boolean" + } + }, + "required": [ + "on_duty" + ], + "type": "object" + }, + "schemas.group.UpdateOperatorSkillLevelMessage": { + "properties": { + "skill_level": { + "description": "New skill level to set the operator in the call center. It must be an integer value between 0 and 100.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "skill_level" + ], + "type": "object" + }, + "schemas.group.UserOrRoomProto": { + "properties": { + "company_id": { + "description": "The company to which this entity belongs.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "country": { + "description": "The country in which the entity resides.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of this entity.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "image_url": { + "description": "The url of this entity's profile image.", + "nullable": true, + "type": "string" + }, + "is_on_duty": { + "description": "Whether the entity is currently acting as an operator.", + "nullable": true, + "type": "boolean" + }, + "name": { + "description": "[single-line only]\n\nThe entity's name.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The office in which this entity resides.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "The phone numbers associated with this entity.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The current enablement state of this entity.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Operator.", + "type": "object" + }, + "schemas.group.VoiceIntelligence": { + "properties": { + "allow_pause": { + "description": "Allow individual users to start and stop Vi during calls. Default is True.", + "nullable": true, + "type": "boolean" + }, + "auto_start": { + "description": "Auto start Vi for this call center. Default is True.", + "nullable": true, + "type": "boolean" + } + }, + "type": "object" + }, + "schemas.member_channel.AddChannelMemberMessage": { + "properties": { + "user_id": { + "description": "The user id.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "title": "Input to add members to a channel", + "type": "object" + }, + "schemas.member_channel.MembersCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of membser from channels.", + "items": { + "$ref": "#/components/schemas/schemas.member_channel.MembersProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of channel members.", + "type": "object" + }, + "schemas.member_channel.MembersProto": { + "properties": { + "id": { + "description": "The user id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nThe user name.", + "nullable": true, + "type": "string" + } + }, + "title": "Channel member.", + "type": "object" + }, + "schemas.member_channel.RemoveChannelMemberMessage": { + "properties": { + "user_id": { + "description": "The user id.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "user_id" + ], + "title": "Input to remove members from a channel", + "type": "object" + }, + "schemas.number.AreaCodeSwap": { + "properties": { + "area_code": { + "description": "An area code in which to find an available phone number for assignment.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Type of swap.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "Swap number with a number in the specified area code.", + "type": "object" + }, + "schemas.number.AssignNumberMessage": { + "properties": { + "area_code": { + "description": "An area code in which to find an available phone number for assignment.", + "nullable": true, + "type": "string" + }, + "number": { + "description": "A phone number to assign. (e164-formatted)", + "nullable": true, + "type": "string" + }, + "primary": { + "default": true, + "description": "A boolean indicating whether this should become the primary phone number.", + "nullable": true, + "type": "boolean" + } + }, + "type": "object" + }, + "schemas.number.AssignNumberTargetGenericMessage": { + "properties": { + "area_code": { + "description": "An area code in which to find an available phone number for assignment.", + "nullable": true, + "type": "string" + }, + "number": { + "description": "A phone number to assign. (e164-formatted)", + "nullable": true, + "type": "string" + }, + "primary": { + "default": true, + "description": "A boolean indicating whether this should become the target's primary phone number.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the target to reassign this number to.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.number.AssignNumberTargetMessage": { + "properties": { + "primary": { + "default": true, + "description": "A boolean indicating whether this should become the target's primary phone number.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the target to reassign this number to.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.number.AutoSwap": { + "properties": { + "type": { + "description": "Type of swap.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "Swap number with an auto-assigned number.", + "type": "object" + }, + "schemas.number.NumberCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of phone numbers.", + "items": { + "$ref": "#/components/schemas/schemas.number.NumberProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of numbers.", + "type": "object" + }, + "schemas.number.NumberProto": { + "properties": { + "area_code": { + "description": "The area code of the number.", + "nullable": true, + "type": "string" + }, + "company_id": { + "description": "The ID of the associated company.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "deleted": { + "description": "A boolean indicating whether this number has been ported out of Dialpad.", + "nullable": true, + "type": "boolean" + }, + "number": { + "description": "The e164-formatted number.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of the associate office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "status": { + "description": "The current assignment status of this number.", + "enum": [ + "available", + "call_center", + "call_router", + "department", + "dynamic_caller", + "office", + "pending", + "porting", + "room", + "user" + ], + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The ID of the target to which this number is assigned.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target to which this number is assigned.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "type": { + "description": "The number type.", + "enum": [ + "free", + "local", + "mobile", + "softbank", + "tollfree" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Number details.", + "type": "object" + }, + "schemas.number.ProvidedNumberSwap": { + "properties": { + "number": { + "description": "A phone number to swap. (e164-formatted)", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Type of swap.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "type" + ], + "title": "Swap number with provided number.", + "type": "object" + }, + "schemas.number.SwapNumberMessage": { + "properties": { + "swap_details": { + "description": "Type of number swap (area_code, auto, provided_number).", + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/schemas.number.AreaCodeSwap" + }, + { + "$ref": "#/components/schemas/schemas.number.AutoSwap" + }, + { + "$ref": "#/components/schemas/schemas.number.ProvidedNumberSwap" + } + ] + }, + "target": { + "$ref": "#/components/schemas/schemas.number.Target", + "description": "The target for swap number.", + "nullable": true, + "type": "object" + } + }, + "required": [ + "target" + ], + "type": "object" + }, + "schemas.number.Target": { + "properties": { + "target_id": { + "description": "The ID of the target to swap number.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The type of the target.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "target_id", + "target_type" + ], + "type": "object" + }, + "schemas.number.UnassignNumberMessage": { + "properties": { + "number": { + "description": "A phone number to unassign. (e164-formatted)", + "nullable": true, + "type": "string" + } + }, + "required": [ + "number" + ], + "type": "object" + }, + "schemas.office.CreateOfficeMessage": { + "properties": { + "annual_commit_monthly_billing": { + "description": "A flag indicating if the primary office's plan is categorized as annual commit monthly billing.", + "nullable": true, + "type": "boolean" + }, + "auto_call_recording": { + "default": false, + "description": "Whether or not automatically record all calls of this office. Default is False.", + "nullable": true, + "type": "boolean" + }, + "billing_address": { + "$ref": "#/components/schemas/schemas.plan.BillingContactMessage", + "description": "The billing address of this created office.", + "nullable": true, + "type": "object" + }, + "billing_contact": { + "$ref": "#/components/schemas/schemas.plan.BillingPointOfContactMessage", + "description": "The billing contact information of this created office.", + "nullable": true, + "type": "object" + }, + "country": { + "description": "The office country.", + "enum": [ + "AR", + "AT", + "AU", + "BD", + "BE", + "BG", + "BH", + "BR", + "CA", + "CH", + "CI", + "CL", + "CN", + "CO", + "CR", + "CY", + "CZ", + "DE", + "DK", + "DO", + "DP", + "EC", + "EE", + "EG", + "ES", + "FI", + "FR", + "GB", + "GH", + "GR", + "GT", + "HK", + "HR", + "HU", + "ID", + "IE", + "IL", + "IN", + "IS", + "IT", + "JP", + "KE", + "KH", + "KR", + "KZ", + "LK", + "LT", + "LU", + "LV", + "MA", + "MD", + "MM", + "MT", + "MX", + "MY", + "NG", + "NL", + "NO", + "NZ", + "PA", + "PE", + "PH", + "PK", + "PL", + "PR", + "PT", + "PY", + "RO", + "RU", + "SA", + "SE", + "SG", + "SI", + "SK", + "SV", + "TH", + "TR", + "TW", + "UA", + "US", + "UY", + "VE", + "VN", + "ZA" + ], + "nullable": true, + "type": "string" + }, + "currency": { + "description": "The office's billing currency.", + "enum": [ + "AUD", + "CAD", + "EUR", + "GBP", + "JPY", + "NZD", + "USD" + ], + "nullable": true, + "type": "string" + }, + "e911_address": { + "$ref": "#/components/schemas/schemas.office.E911Message", + "description": "The emergency address of the created office.\n\nRequired for country codes of US, CA, AU, FR, GB, NZ.", + "nullable": true, + "type": "object" + }, + "first_action": { + "description": "The desired action when the office receives a call.", + "enum": [ + "menu", + "operators" + ], + "nullable": true, + "type": "string" + }, + "friday_hours": { + "description": "The Friday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_description": { + "description": "The description of the office. Max 256 characters.", + "nullable": true, + "type": "string" + }, + "hours_on": { + "default": false, + "description": "The time frame when the office wants to receive calls. Default value is false, which means the office will always take calls (24/7).", + "nullable": true, + "type": "boolean" + }, + "international_enabled": { + "description": "A flag indicating if the primary office is able to make international phone calls.", + "nullable": true, + "type": "boolean" + }, + "invoiced": { + "description": "A flag indicating if the payment will be paid by invoice.", + "nullable": true, + "type": "boolean" + }, + "mainline_number": { + "description": "The mainline of the office.", + "nullable": true, + "type": "string" + }, + "monday_hours": { + "description": "The Monday hours of operation. To specify when hours_on is set to True. e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe office name.", + "nullable": true, + "type": "string" + }, + "no_operators_action": { + "description": "The action to take if there is no one available to answer calls.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "plan_period": { + "description": "The frequency at which the company will be billed.", + "enum": [ + "monthly", + "yearly" + ], + "nullable": true, + "type": "string" + }, + "ring_seconds": { + "description": "The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Call routing options for this group.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "sunday_hours": { + "description": "The Sunday hours of operation. Default is empty array.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "timezone": { + "description": "Timezone using a tz database name.", + "nullable": true, + "type": "string" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "unified_billing": { + "description": "A flag indicating if to send a unified invoice.", + "nullable": true, + "type": "boolean" + }, + "use_same_address": { + "description": "A flag indicating if the billing address and the emergency address are the same.", + "nullable": true, + "type": "boolean" + }, + "voice_intelligence": { + "$ref": "#/components/schemas/schemas.group.VoiceIntelligence", + "description": "Configure voice intelligence.", + "nullable": true, + "type": "object" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation. Default value is [\"08:00\", \"18:00\"].", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "required": [ + "annual_commit_monthly_billing", + "billing_address", + "country", + "currency", + "invoiced", + "name", + "plan_period", + "unified_billing" + ], + "title": "Secondary Office creation.", + "type": "object" + }, + "schemas.office.E911GetProto": { + "properties": { + "address": { + "description": "[single-line only]\n\nLine 1 of the E911 address.", + "nullable": true, + "type": "string" + }, + "address2": { + "description": "[single-line only]\n\nLine 2 of the E911 address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nCity of the E911 address.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Country of the E911 address.", + "nullable": true, + "type": "string" + }, + "state": { + "description": "[single-line only]\n\nState or Province of the E911 address.", + "nullable": true, + "type": "string" + }, + "zip": { + "description": "[single-line only]\n\nZip code of the E911 address.", + "nullable": true, + "type": "string" + } + }, + "title": "E911 address.", + "type": "object" + }, + "schemas.office.E911Message": { + "properties": { + "address": { + "description": "[single-line only]\n\nLine 1 of the E911 address.", + "nullable": true, + "type": "string" + }, + "address2": { + "description": "[single-line only]\n\nLine 2 of the E911 address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nCity of the E911 address.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Country of the E911 address.", + "nullable": true, + "type": "string" + }, + "state": { + "description": "[single-line only]\n\nState or Province of the E911 address.", + "nullable": true, + "type": "string" + }, + "zip": { + "description": "[single-line only]\n\nZip code of the E911 address.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "address", + "city", + "country", + "state", + "zip" + ], + "title": "E911 address.", + "type": "object" + }, + "schemas.office.E911UpdateMessage": { + "properties": { + "address": { + "description": "[single-line only]\n\nLine 1 of the new E911 address.", + "nullable": true, + "type": "string" + }, + "address2": { + "default": "", + "description": "[single-line only]\n\nLine 2 of the new E911 address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nCity of the new E911 address.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Country of the new E911 address.", + "nullable": true, + "type": "string" + }, + "state": { + "description": "[single-line only]\n\nState or Province of the new E911 address.", + "nullable": true, + "type": "string" + }, + "update_all": { + "description": "Update E911 for all users in this office.", + "nullable": true, + "type": "boolean" + }, + "use_validated_option": { + "description": "Whether to use the validated address option from our service.", + "nullable": true, + "type": "boolean" + }, + "zip": { + "description": "[single-line only]\n\nZip code of the new E911 address.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "address", + "city", + "country", + "state", + "zip" + ], + "type": "object" + }, + "schemas.office.OffDutyStatusesProto": { + "properties": { + "id": { + "description": "The office ID.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "off_duty_statuses": { + "description": "The off-duty statuses configured for this office.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Off-duty statuses.", + "type": "object" + }, + "schemas.office.OfficeCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of offices.", + "items": { + "$ref": "#/components/schemas/schemas.office.OfficeProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of offices.", + "type": "object" + }, + "schemas.office.OfficeProto": { + "properties": { + "availability_status": { + "description": "Availability status of the office.", + "enum": [ + "closed", + "holiday_closed", + "holiday_open", + "open" + ], + "nullable": true, + "type": "string" + }, + "country": { + "description": "The country in which the office is situated.", + "nullable": true, + "type": "string" + }, + "e911_address": { + "$ref": "#/components/schemas/schemas.office.E911GetProto", + "description": "The e911 address of the office.", + "nullable": true, + "type": "object" + }, + "first_action": { + "description": "The desired action when the office receives a call.", + "enum": [ + "menu", + "operators" + ], + "nullable": true, + "type": "string" + }, + "friday_hours": { + "description": "The Friday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "id": { + "description": "The office's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "is_primary_office": { + "description": "A flag indicating if the office is a primary office of its company.", + "nullable": true, + "type": "boolean" + }, + "monday_hours": { + "description": "The Monday hours of operation.\n(e.g. [\"08:00\", \"12:00\", \"14:00\", \"18:00\"] => open from 8AM to Noon, and from 2PM to 6PM.)", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "name": { + "description": "[single-line only]\n\nThe name of the office.", + "nullable": true, + "type": "string" + }, + "no_operators_action": { + "description": "The action to take if there is no one available to answer calls.", + "enum": [ + "bridge_target", + "company_directory", + "department", + "directory", + "disabled", + "extension", + "menu", + "message", + "operator", + "person", + "scripted_ivr", + "voicemail" + ], + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The office's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "office_settings": { + "$ref": "#/components/schemas/schemas.office.OfficeSettings", + "description": "Office-specific settings object.", + "nullable": true, + "type": "object" + }, + "phone_numbers": { + "description": "The phone number(s) assigned to this office.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "ring_seconds": { + "description": "The number of seconds to ring the main line before going to voicemail.\n(or an other-wise-specified no_operators_action).", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "routing_options": { + "$ref": "#/components/schemas/schemas.group.RoutingOptions", + "description": "Specific call routing action to take when the office is open or closed.", + "nullable": true, + "type": "object" + }, + "saturday_hours": { + "description": "The Saturday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The enablement-state of the office.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "sunday_hours": { + "description": "The Sunday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "thursday_hours": { + "description": "The Thursday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "timezone": { + "description": "Timezone of the office.", + "nullable": true, + "type": "string" + }, + "tuesday_hours": { + "description": "The Tuesday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "wednesday_hours": { + "description": "The Wednesday hours of operation.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Office.", + "type": "object" + }, + "schemas.office.OfficeSettings": { + "properties": { + "allow_device_guest_login": { + "description": "Allows guests to use desk phones within the office.", + "nullable": true, + "type": "boolean" + }, + "block_caller_id_disabled": { + "description": "Whether the block-caller-ID option is disabled.", + "nullable": true, + "type": "boolean" + }, + "bridged_target_recording_allowed": { + "description": "Whether recordings are enabled for sub-groups of this office.\n(e.g. departments or call centers).", + "nullable": true, + "type": "boolean" + }, + "disable_desk_phone_self_provision": { + "description": "Whether desk-phone self-provisioning is disabled.", + "nullable": true, + "type": "boolean" + }, + "disable_ivr_voicemail": { + "description": "Whether the default IVR voicemail feature is disabled.", + "nullable": true, + "type": "boolean" + }, + "no_recording_message_on_user_calls": { + "description": "Whether recording of user calls should be disabled.", + "nullable": true, + "type": "boolean" + }, + "set_caller_id_disabled": { + "description": "Whether the caller-ID option is disabled.", + "nullable": true, + "type": "boolean" + } + }, + "type": "object" + }, + "schemas.office.OfficeUpdateResponse": { + "properties": { + "office": { + "$ref": "#/components/schemas/schemas.office.OfficeProto", + "description": "The updated office object.", + "nullable": true, + "type": "object" + }, + "plan": { + "$ref": "#/components/schemas/schemas.plan.PlanProto", + "description": "The updated office plan object.", + "nullable": true, + "type": "object" + } + }, + "title": "Office update.", + "type": "object" + }, + "schemas.plan.AvailableLicensesProto": { + "properties": { + "additional_number_lines": { + "description": "The number of additional-number lines allocated for this plan.\n\nadditional-number lines are consumed when multiple numbers are assigned to a target. i.e. if any callable entity has more than one direct number, one additional-number line is consumed for each number after the first number. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "contact_center_lines": { + "description": "The number of contact-center lines allocated for this plan.\n\nContact-center lines are consumed for new users that can serve as call center agents, but does\n*not* include a primary number for the user. This line type is only available for pro and enterprise accounts.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "fax_lines": { + "description": "The number of fax lines allocated for this plan.\n\nFax lines are consumed when a fax number is assigned to a user, office, department etc. Fax lines can be used with or without a physical fax machine, as received faxes are exposed as PDFs in the Dialpad app. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "room_lines": { + "description": "The number of room lines allocated for this plan.\n\nRoom lines are consumed when a new room with a dedicated number is created. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "sell_lines": { + "description": "The number of sell lines allocated for this plan.\n\nSell lines are consumed for new users that can serve as call center agents and includes a primary number for that user. This line type is only available for pro and enterprise accounts.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "talk_lines": { + "description": "The number of talk lines allocated for this plan.\n\nTalk lines are consumed when a new user with a primary number is created. This line type is available for all account types, and does not include the ability for the user to be a call center agent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_additional_number_lines": { + "description": "The number of toll-free-additional-number lines allocated for this plan.\n\nThese are functionally equivalent to additional-number lines, except that the number is a toll-free number. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_room_lines": { + "description": "The number of toll-free room lines allocated for this plan.\n\nThese are functionally equivalent to room lines, except that the room's primary number is a toll-free number (subsequent numbers for a given room will still consume additional-number/toll-free-additional-number lines rather than multiple room lines). This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_uberconference_lines": { + "description": "The number of toll-free uberconference lines allocated for this plan.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "uberconference_lines": { + "description": "The number of uberconference lines available for this office.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Available licenses.", + "type": "object" + }, + "schemas.plan.BillingContactMessage": { + "properties": { + "address_line_1": { + "description": "[single-line only]\n\nThe first line of the billing address.", + "nullable": true, + "type": "string" + }, + "address_line_2": { + "description": "[single-line only]\n\nThe second line of the billing address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nThe billing address city.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "The billing address country.", + "nullable": true, + "type": "string" + }, + "postal_code": { + "description": "[single-line only]\n\nThe billing address postal code.", + "nullable": true, + "type": "string" + }, + "region": { + "description": "[single-line only]\n\nThe billing address region.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "address_line_1", + "city", + "country", + "postal_code", + "region" + ], + "title": "Billing contact.", + "type": "object" + }, + "schemas.plan.BillingContactProto": { + "properties": { + "address_line_1": { + "description": "[single-line only]\n\nThe first line of the billing address.", + "nullable": true, + "type": "string" + }, + "address_line_2": { + "description": "[single-line only]\n\nThe second line of the billing address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nThe billing address city.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "The billing address country.", + "nullable": true, + "type": "string" + }, + "postal_code": { + "description": "[single-line only]\n\nThe billing address postal code.", + "nullable": true, + "type": "string" + }, + "region": { + "description": "[single-line only]\n\nThe billing address region.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.plan.BillingPointOfContactMessage": { + "properties": { + "email": { + "description": "The contact email.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nThe contact name.", + "nullable": true, + "type": "string" + }, + "phone": { + "description": "The contact phone number.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "email", + "name" + ], + "type": "object" + }, + "schemas.plan.PlanProto": { + "properties": { + "additional_number_lines": { + "description": "The number of additional-number lines allocated for this plan.\n\nadditional-number lines are consumed when multiple numbers are assigned to a target. i.e. if any callable entity has more than one direct number, one additional-number line is consumed for each number after the first number. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "balance": { + "description": "The remaining balance for this plan.\n\nThe balance will be expressed as string-encoded floating point values and will be provided in terms of USD.", + "nullable": true, + "type": "string" + }, + "contact_center_lines": { + "description": "The number of contact-center lines allocated for this plan.\n\nContact-center lines are consumed for new users that can serve as call center agents, but does\n*not* include a primary number for the user. This line type is only available for pro and enterprise accounts.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "fax_lines": { + "description": "The number of fax lines allocated for this plan.\n\nFax lines are consumed when a fax number is assigned to a user, office, department etc. Fax lines can be used with or without a physical fax machine, as received faxes are exposed as PDFs in the Dialpad app. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "next_billing_date": { + "description": "The UTC timestamp of the start of the next billing cycle.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "ppu_address": { + "$ref": "#/components/schemas/schemas.plan.BillingContactProto", + "description": "The \"Place of Primary Use\" address.", + "nullable": true, + "type": "object" + }, + "room_lines": { + "description": "The number of room lines allocated for this plan.\n\nRoom lines are consumed when a new room with a dedicated number is created. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "sell_lines": { + "description": "The number of sell lines allocated for this plan.\n\nSell lines are consumed for new users that can serve as call center agents and includes a primary number for that user. This line type is only available for pro and enterprise accounts.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "talk_lines": { + "description": "The number of talk lines allocated for this plan.\n\nTalk lines are consumed when a new user with a primary number is created. This line type is available for all account types, and does not include the ability for the user to be a call center agent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_additional_number_lines": { + "description": "The number of toll-free-additional-number lines allocated for this plan.\n\nThese are functionally equivalent to additional-number lines, except that the number is a toll-free number. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_room_lines": { + "description": "The number of toll-free room lines allocated for this plan.\n\nThese are functionally equivalent to room lines, except that the room's primary number is a toll-free number (subsequent numbers for a given room will still consume additional-number/toll-free-additional-number lines rather than multiple room lines). This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "tollfree_uberconference_lines": { + "description": "The number of toll-free uberconference lines allocated for this plan.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "uberconference_lines": { + "description": "The number of uberconference lines available for this office.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Billing plan.", + "type": "object" + }, + "schemas.recording_share_link.CreateRecordingShareLink": { + "properties": { + "privacy": { + "default": "owner", + "description": "The privacy state of the recording share link.", + "enum": [ + "admin", + "company", + "owner", + "public" + ], + "nullable": true, + "type": "string" + }, + "recording_id": { + "description": "The recording entity's ID.", + "nullable": true, + "type": "string" + }, + "recording_type": { + "description": "The type of the recording entity shared via the link.", + "enum": [ + "admincallrecording", + "callrecording", + "voicemail" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "recording_id", + "recording_type" + ], + "type": "object" + }, + "schemas.recording_share_link.RecordingShareLink": { + "properties": { + "access_link": { + "description": "The access link where recording can be listened or downloaded.", + "nullable": true, + "type": "string" + }, + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "created_by_id": { + "description": "The ID of the target who created the link.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "date_added": { + "description": "The date when the recording share link is created.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The recording share link's ID.", + "nullable": true, + "type": "string" + }, + "item_id": { + "description": "The ID of the recording entity shared via the link.", + "nullable": true, + "type": "string" + }, + "privacy": { + "description": "The privacy state of the recording share link.", + "enum": [ + "admin", + "company", + "owner", + "public" + ], + "nullable": true, + "type": "string" + }, + "type": { + "description": "The type of the recording entity shared via the link.", + "enum": [ + "admincallrecording", + "callrecording", + "voicemail" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Recording share link.", + "type": "object" + }, + "schemas.recording_share_link.UpdateRecordingShareLink": { + "properties": { + "privacy": { + "description": "The privacy state of the recording share link.", + "enum": [ + "admin", + "company", + "owner", + "public" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "privacy" + ], + "type": "object" + }, + "schemas.room.CreateInternationalPinProto": { + "properties": { + "customer_ref": { + "description": "[single-line only]\n\nAn identifier to be printed in the usage summary. Typically used for identifying the person who requested the PIN.", + "nullable": true, + "type": "string" + } + }, + "title": "Input to create a PIN for protected international calls from room.", + "type": "object" + }, + "schemas.room.CreateRoomMessage": { + "properties": { + "name": { + "description": "[single-line only]\n\nThe name of the room.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The office in which this room resides.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "name", + "office_id" + ], + "type": "object" + }, + "schemas.room.InternationalPinProto": { + "properties": { + "customer_ref": { + "description": "[single-line only]\n\nAn identifier to be printed in the usage summary. Typically used for identifying the person who requested the PIN.", + "nullable": true, + "type": "string" + }, + "expires_on": { + "description": "A time after which the PIN will no longer be valid.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "pin": { + "description": "A PIN that must be entered to make international calls.", + "nullable": true, + "type": "string" + } + }, + "title": "Full response body for get pin operation.", + "type": "object" + }, + "schemas.room.RoomCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of rooms.", + "items": { + "$ref": "#/components/schemas/schemas.room.RoomProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of rooms.", + "type": "object" + }, + "schemas.room.RoomProto": { + "properties": { + "company_id": { + "description": "The ID of this room's company.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "country": { + "description": "The country in which the room resides.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the room.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "image_url": { + "description": "The profile image to use when displaying this room in the Dialpad app.", + "nullable": true, + "type": "string" + }, + "is_free": { + "description": "A boolean indicating whether this room is consuming a license with an associated cost.", + "nullable": true, + "type": "boolean" + }, + "is_on_duty": { + "description": "A boolean indicating whether this room is actively acting as an operator.", + "nullable": true, + "type": "boolean" + }, + "name": { + "description": "[single-line only]\n\nThe name of the room.", + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The ID of this room's office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "The phone numbers assigned to this room.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The current enablement state of this room.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Room.", + "type": "object" + }, + "schemas.room.UpdateRoomMessage": { + "properties": { + "name": { + "description": "[single-line only]\n\nThe name of the room.", + "nullable": true, + "type": "string" + }, + "phone_numbers": { + "description": "A list of all phone numbers assigned to the room.\n\nNumbers can be re-ordered or removed from this list to unassign them.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + } + }, + "type": "object" + }, + "schemas.schedule_reports.ProcessScheduleReportsMessage": { + "properties": { + "at": { + "description": "Hour of the day when the report will execute considering the frequency and timezones between 0 and 23 e.g. 10 will be 10:00 am.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "coaching_group": { + "default": false, + "description": "Whether the the statistics should be for trainees of the coach group with the given target_id.", + "nullable": true, + "type": "boolean" + }, + "enabled": { + "default": true, + "description": "Whether or not this schedule reports event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "frequency": { + "description": "How often the report will execute.", + "enum": [ + "daily", + "monthly", + "weekly" + ], + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nThe name of the schedule reports.", + "nullable": true, + "type": "string" + }, + "on_day": { + "description": "The day of the week or month when the report will execute considering the frequency. daily=0, weekly=0-6, monthly=0-30.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "report_type": { + "description": "The type of report that will be generated.", + "enum": [ + "call_logs", + "daily_statistics", + "recordings", + "user_statistics", + "voicemails" + ], + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The target's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "timezone": { + "description": "Timezone using a tz database name.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "at", + "endpoint_id", + "frequency", + "name", + "on_day", + "report_type" + ], + "type": "object" + }, + "schemas.schedule_reports.ScheduleReportsCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of results.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of schedule reports.", + "items": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Schedule reports collection.", + "type": "object" + }, + "schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto": { + "properties": { + "at": { + "description": "Hour of the day when the report will execute considering the frequency and timezones between 0 and 23 e.g. 10 will be 10:00 am.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "coaching_group": { + "default": false, + "description": "Whether the the statistics should be for trainees of the coach group with the given target_id.", + "nullable": true, + "type": "boolean" + }, + "enabled": { + "default": true, + "description": "Whether or not the this agent status event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "frequency": { + "description": "The frequency of the schedule reports.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The schedule reports subscription's ID, which is generated after creating an schedule reports subscription successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "name": { + "description": "[single-line only]\n\nThe day to be send the schedule reports.", + "nullable": true, + "type": "string" + }, + "on_day": { + "description": "The day of the week or month when the report will execute considering the frequency. daily=0, weekly=0-6, monthly=0-30.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "report_type": { + "description": "The report options filters.", + "enum": [ + "call_logs", + "daily_statistics", + "recordings", + "user_statistics", + "voicemails" + ], + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The target's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "company", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "timezone": { + "description": "Timezone using a tz database name.", + "nullable": true, + "type": "string" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "required": [ + "report_type" + ], + "title": "Schedule report status event subscription.", + "type": "object" + }, + "schemas.screen_pop.InitiateScreenPopMessage": { + "properties": { + "screen_pop_uri": { + "description": "The screen pop's url.\n\nMost Url should start with scheme name such as http or https. Be aware that url with userinfo subcomponent, such as\n\"https://username:password@www.example.com\" is not supported for security reasons. Launching native apps is also supported through a format such as \"customuri://domain.com\"", + "nullable": true, + "type": "string" + } + }, + "required": [ + "screen_pop_uri" + ], + "type": "object" + }, + "schemas.screen_pop.InitiateScreenPopResponse": { + "properties": { + "device": { + "$ref": "#/components/schemas/schemas.userdevice.UserDeviceProto", + "description": "A device owned by the user.", + "nullable": true, + "type": "object" + } + }, + "title": "Screen pop initiation.", + "type": "object" + }, + "schemas.signature.SignatureProto": { + "properties": { + "algo": { + "description": "The hash algorithm used to compute the signature.", + "nullable": true, + "type": "string" + }, + "secret": { + "description": "[single-line only]\n\nThe secret string that will be used to sign the payload.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "The signature token type.\n\n(i.e. `jwt`)", + "nullable": true, + "type": "string" + } + }, + "title": "Signature settings.", + "type": "object" + }, + "schemas.sms.SMSProto": { + "properties": { + "contact_id": { + "description": "The ID of the specific contact which SMS should be sent to.", + "nullable": true, + "type": "string" + }, + "created_date": { + "description": "Date of SMS creation.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "device_type": { + "description": "The device type.", + "enum": [ + "android", + "ata", + "audiocodes", + "c2t", + "ciscompp", + "dect", + "dpmroom", + "grandstream", + "harness", + "iframe_cti_extension", + "iframe_front", + "iframe_hubspot", + "iframe_ms_teams", + "iframe_open_cti", + "iframe_salesforce", + "iframe_service_titan", + "iframe_zendesk", + "ipad", + "iphone", + "mini", + "mitel", + "msteams", + "native", + "obi", + "packaged_app", + "polyandroid", + "polycom", + "proxy", + "public_api", + "salesforce", + "sip", + "tickiot", + "web", + "yealink" + ], + "nullable": true, + "type": "string" + }, + "direction": { + "description": "SMS direction.", + "enum": [ + "inbound", + "outbound" + ], + "nullable": true, + "type": "string" + }, + "from_number": { + "description": "The phone number from which the SMS was sent.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the SMS.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "message_delivery_result": { + "description": "The final message delivery result.", + "enum": [ + "accepted", + "internal_error", + "invalid_destination", + "invalid_source", + "no_route", + "not_supported", + "rejected", + "rejected_spam", + "time_out" + ], + "nullable": true, + "type": "string" + }, + "message_status": { + "description": "The status of the SMS.", + "enum": [ + "failed", + "pending", + "success" + ], + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The target's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "text": { + "description": "The contents of the message that was sent.", + "nullable": true, + "type": "string" + }, + "to_numbers": { + "description": "Up to 10 E164-formatted phone numbers who received the SMS.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "user_id": { + "description": "The ID of the user who sent the SMS.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "SMS message.", + "type": "object" + }, + "schemas.sms.SendSMSMessage": { + "properties": { + "channel_hashtag": { + "description": "[single-line only]\n\nThe hashtag of the channel which should receive the SMS.", + "nullable": true, + "type": "string" + }, + "from_number": { + "description": "The number of who sending the SMS. The number must be assigned to user or a user group. It will override user_id and sender_group_id.", + "nullable": true, + "type": "string" + }, + "infer_country_code": { + "default": false, + "description": "If true, to_numbers will be assumed to be from the specified user's country, and the E164 format requirement will be relaxed.", + "nullable": true, + "type": "boolean" + }, + "media": { + "description": "Base64-encoded media attachment (will cause the message to be sent as MMS).\n(Max 500 KiB raw file size)", + "format": "byte", + "nullable": true, + "type": "string" + }, + "sender_group_id": { + "description": "The ID of an office, department, or call center that the User should send the message on behalf of.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "sender_group_type": { + "description": "The sender group's type (i.e. office, department, or callcenter).", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + }, + "text": { + "default": "", + "description": "The contents of the message that should be sent.", + "nullable": true, + "type": "string" + }, + "to_numbers": { + "description": "Up to 10 E164-formatted phone numbers who should receive the SMS.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "user_id": { + "description": "The ID of the user who should be the sender of the SMS.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.sms_event_subscription.CreateSmsEventSubscription": { + "properties": { + "direction": { + "description": "The SMS direction this event subscription subscribes to.", + "enum": [ + "all", + "inbound", + "outbound" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the SMS event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "include_internal": { + "default": false, + "description": "Whether or not to trigger SMS events for SMS sent between two users from the same company.", + "nullable": true, + "type": "boolean" + }, + "status": { + "default": false, + "description": "Whether or not to update on each SMS delivery status.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "direction" + ], + "type": "object" + }, + "schemas.sms_event_subscription.SmsEventSubscriptionCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of SMS event subscriptions.", + "items": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of sms event subscriptions.", + "type": "object" + }, + "schemas.sms_event_subscription.SmsEventSubscriptionProto": { + "properties": { + "direction": { + "description": "The SMS direction this event subscription subscribes to.", + "enum": [ + "all", + "inbound", + "outbound" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the SMS event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "id": { + "description": "The ID of the SMS event subscription.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "include_internal": { + "default": false, + "description": "Whether or not to trigger SMS events for SMS sent between two users from the same company.", + "nullable": true, + "type": "boolean" + }, + "status": { + "default": false, + "description": "Whether or not to update on each SMS delivery status.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "webhook": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "description": "The webhook that's associated with this event subscription.", + "nullable": true, + "type": "object" + }, + "websocket": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "description": "The websocket's ID, which is generated after creating a webhook successfully.", + "nullable": true, + "type": "object" + } + }, + "type": "object" + }, + "schemas.sms_event_subscription.UpdateSmsEventSubscription": { + "properties": { + "direction": { + "description": "The SMS direction this event subscription subscribes to.", + "enum": [ + "all", + "inbound", + "outbound" + ], + "nullable": true, + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether or not the SMS event subscription is enabled.", + "nullable": true, + "type": "boolean" + }, + "endpoint_id": { + "description": "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "include_internal": { + "default": false, + "description": "Whether or not to trigger SMS events for SMS sent between two users from the same company.", + "nullable": true, + "type": "boolean" + }, + "status": { + "default": false, + "description": "Whether or not to update on each SMS delivery status.", + "nullable": true, + "type": "boolean" + }, + "target_id": { + "description": "The ID of the specific target for which events should be sent.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "The target's type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.sms_opt_out.OptOutScopeInfo": { + "description": "Note, this info should be present for a particular entry in the result set if and only if the given external endpoint is actually opted out (i.e. see OptOutState.opted_out documentation); in other words, this does not apply for results in the 'opted_back_in' state.", + "properties": { + "opt_out_scope_level": { + "description": "Scope level that the external endpoint is opted out of.", + "enum": [ + "a2p_campaign", + "company" + ], + "nullable": true, + "type": "string" + }, + "scope_id": { + "description": "Unique ID of the scope entity (Company or A2P Campaign).\n\nNote, this refers to the ID assigned to this entity by Dialpad, as opposed to the TCR-assigned id.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "opt_out_scope_level", + "scope_id" + ], + "title": "Description of the opt-out scope.", + "type": "object" + }, + "schemas.sms_opt_out.SmsOptOutEntryProto": { + "properties": { + "date": { + "description": "An optional timestamp in (milliseconds-since-epoch UTC format) representing the time at which the given external endpoint transitioned to the opt_out_state.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "external_endpoint": { + "description": "An E.164-formatted DID representing the 'external endpoint' used to contact the 'external user'\n.", + "nullable": true, + "type": "string" + }, + "opt_out_scope_info": { + "$ref": "#/components/schemas/schemas.sms_opt_out.OptOutScopeInfo", + "description": "Description of the scope of communications that this external endpoint is opted out from.\n\nAs explained in the OptOutScopeInfo documentation, this must be provided if this list entry describes an endpoint that is opted out of some scope (indicated by the value of 'opt_out_state'). If the 'opt_out_state' for this entry is not 'opted_out', then this parameter will be excluded entirely or set to a null value.\n\nFor SMS opt-out-import requests: in the A2P-campaign-scope case, opt_out_scope_info.id must refer to the id of a valid, registered A2P campaign entity owned by this company. In the company-scope case, opt_out_scope_info.id must be set to the company id.", + "nullable": true, + "type": "object" + }, + "opt_out_state": { + "description": "Opt-out state for this entry in the list.", + "enum": [ + "opted_back_in", + "opted_out" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "external_endpoint", + "opt_out_state" + ], + "title": "Individual sms-opt-out list entry.", + "type": "object" + }, + "schemas.sms_opt_out.SmsOptOutListProto": { + "properties": { + "cursor": { + "description": "A token that can be used to return the next page of results, if there are any remaining; to fetch the next page, the requester must pass this value as an argument in a new request.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "List of sms opt-out entries.", + "items": { + "$ref": "#/components/schemas/schemas.sms_opt_out.SmsOptOutEntryProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "A list of sms-opt-out entries to be returned in the API response.", + "type": "object" + }, + "schemas.stats.ProcessStatsMessage": { + "properties": { + "coaching_group": { + "description": "Whether or not the the statistics should be for trainees of the coach group with the given target_id.", + "nullable": true, + "type": "boolean" + }, + "coaching_team": { + "description": "Whether or not the the statistics should be for trainees of the coach team with the given target_id.", + "nullable": true, + "type": "boolean" + }, + "days_ago_end": { + "default": 30, + "description": "End of the date range to get statistics for.\n\nThis is the number of days to look back relative to the current day. Used in conjunction with days_ago_start to specify a range.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "days_ago_start": { + "default": 1, + "description": "Start of the date range to get statistics for.\n\nThis is the number of days to look back relative to the current day. Used in conjunction with days_ago_end to specify a range.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "export_type": { + "description": "Whether to return aggregated statistics (stats), or individual rows for each record (records).\n\nNOTE: For stat_type \"csat\" or \"dispositions\", only \"records\" is supported.", + "enum": [ + "records", + "stats" + ], + "nullable": true, + "type": "string" + }, + "group_by": { + "description": "This param is only applicable when the stat_type is specified as call. For call stats, group calls by user per day (default), get total metrics by day, or break down by department and call center (office only).", + "enum": [ + "date", + "group", + "user" + ], + "nullable": true, + "type": "string" + }, + "is_today": { + "description": "Whether or not the statistics are for the current day.\n\nNOTE: days_ago_start and days_ago_end are ignored if this is passed in.", + "nullable": true, + "type": "boolean" + }, + "office_id": { + "description": "ID of the office to get statistics for.\n\nIf a target_id and target_type are passed in this value is ignored and instead the target is used.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "stat_type": { + "description": "The type of statistics to be returned.\n\nNOTE: if the value is \"csat\" or \"dispositions\", target_id and target_type must be specified.", + "enum": [ + "calls", + "csat", + "dispositions", + "onduty", + "recordings", + "screenshare", + "texts", + "voicemails" + ], + "nullable": true, + "type": "string" + }, + "target_id": { + "description": "The target's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "target_type": { + "description": "Target's type.", + "enum": [ + "callcenter", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "timezone": { + "default": "UTC", + "description": "Timezone using a tz database name.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "export_type", + "stat_type" + ], + "type": "object" + }, + "schemas.stats.ProcessingProto": { + "properties": { + "already_started": { + "description": "A boolean indicating whether this request has already begun processing.", + "nullable": true, + "type": "boolean" + }, + "request_id": { + "description": "The processing request ID.", + "nullable": true, + "type": "string" + } + }, + "title": "Processing status.", + "type": "object" + }, + "schemas.stats.StatsProto": { + "properties": { + "download_url": { + "description": "The URL of the resulting stats file.", + "nullable": true, + "type": "string" + }, + "file_type": { + "description": "The file format of the resulting stats file.", + "nullable": true, + "type": "string" + }, + "status": { + "description": "The current status of the processing request.", + "enum": [ + "complete", + "failed", + "processing" + ], + "nullable": true, + "type": "string" + } + }, + "title": "Stats export.", + "type": "object" + }, + "schemas.transcript.TranscriptLineProto": { + "properties": { + "contact_id": { + "description": "The ID of the contact who was speaking.", + "nullable": true, + "type": "string" + }, + "content": { + "description": "The transcribed text.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "The name of the call participant who was speaking.", + "nullable": true, + "type": "string" + }, + "time": { + "description": "The time at which the line was spoken.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Either \"moment\" or \"transcript\".", + "enum": [ + "ai_question", + "custom_moment", + "moment", + "real_time_moment", + "transcript" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The ID of the user who was speaking.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Transcript line.", + "type": "object" + }, + "schemas.transcript.TranscriptProto": { + "properties": { + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "lines": { + "description": "An array of individual lines of the transcript.", + "items": { + "$ref": "#/components/schemas/schemas.transcript.TranscriptLineProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Transcript.", + "type": "object" + }, + "schemas.transcript.TranscriptUrlProto": { + "properties": { + "call_id": { + "description": "The call's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "url": { + "description": "The url with which the call transcript can be accessed.", + "nullable": true, + "type": "string" + } + }, + "title": "Transcript URL.", + "type": "object" + }, + "schemas.uberconference.meeting.MeetingParticipantProto": { + "properties": { + "call_in_method": { + "description": "The method this participant used to joined the meeting.", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "Name of the meeting participant.", + "nullable": true, + "type": "string" + }, + "email": { + "description": "The email address of the participant. (if applicable)", + "nullable": true, + "type": "string" + }, + "is_organizer": { + "description": "Whether or not the participant is the meeting's organizer.", + "nullable": true, + "type": "boolean" + }, + "name": { + "description": "Name of the meeting participant.", + "nullable": true, + "type": "string" + }, + "phone": { + "description": "The number that the participant dialed in from. (if applicable)", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The number that the participant dialed in from. (if applicable)", + "nullable": true, + "type": "string" + }, + "talk_time": { + "description": "The amount of time this participant was speaking. (in milliseconds)", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Public API representation of an UberConference meeting participant.", + "type": "object" + }, + "schemas.uberconference.meeting.MeetingRecordingProto": { + "properties": { + "size": { + "description": "Human-readable size of the recording files. (e.g. 14.3MB)", + "nullable": true, + "type": "string" + }, + "url": { + "description": "The URL of the audio recording of the meeting.", + "nullable": true, + "type": "string" + } + }, + "title": "Public API representation of an UberConference meeting recording.", + "type": "object" + }, + "schemas.uberconference.meeting.MeetingSummaryCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of meeting summaries.", + "items": { + "$ref": "#/components/schemas/schemas.uberconference.meeting.MeetingSummaryProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of rooms for get all room operations.", + "type": "object" + }, + "schemas.uberconference.meeting.MeetingSummaryProto": { + "properties": { + "duration_ms": { + "description": "The duration of the meeting in milliseconds.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "end_time": { + "description": "The time at which the meeting was ended. (ISO-8601 format)", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "host_name": { + "description": "The name of the host of the meeting.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the meeting.", + "nullable": true, + "type": "string" + }, + "participants": { + "description": "The list of users that participated in the meeting.", + "items": { + "$ref": "#/components/schemas/schemas.uberconference.meeting.MeetingParticipantProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "recordings": { + "description": "A list of recordings from the meeting.", + "items": { + "$ref": "#/components/schemas/schemas.uberconference.meeting.MeetingRecordingProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "room_id": { + "description": "The ID of the conference room in which the meeting took place.", + "nullable": true, + "type": "string" + }, + "start_time": { + "description": "The time at which the first participant joined the meeting. (ISO-8601 format)", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "title": { + "description": "The name of the meeting.", + "nullable": true, + "type": "string" + }, + "transcript_url": { + "description": "The URL of the meeting transcript.", + "nullable": true, + "type": "string" + } + }, + "title": "Public API representation of an UberConference meeting.", + "type": "object" + }, + "schemas.uberconference.room.RoomCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of meeting rooms.", + "items": { + "$ref": "#/components/schemas/schemas.uberconference.room.RoomProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of rooms for get all room operations.", + "type": "object" + }, + "schemas.uberconference.room.RoomProto": { + "properties": { + "company_name": { + "description": "The name of the company that owns the room.", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "The name of the room.", + "nullable": true, + "type": "string" + }, + "email": { + "description": "The email associated with the room owner.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the meeting room.", + "nullable": true, + "type": "string" + }, + "number": { + "description": "The e164-formatted dial-in number for the room.", + "nullable": true, + "type": "string" + }, + "path": { + "description": "The access URL for the meeting room.", + "nullable": true, + "type": "string" + } + }, + "title": "Public API representation of an UberConference room.", + "type": "object" + }, + "schemas.user.CreateUserMessage": { + "properties": { + "auto_assign": { + "default": false, + "description": "If set to true, a number will be automatically assigned.", + "nullable": true, + "type": "boolean" + }, + "email": { + "description": "The user's email.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe user's first name.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe user's last name.", + "nullable": true, + "type": "string" + }, + "license": { + "default": "talk", + "description": "The user's license type. This affects billing for the user.", + "enum": [ + "admins", + "agents", + "dpde_all", + "dpde_one", + "lite_lines", + "lite_support_agents", + "magenta_lines", + "talk" + ], + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The user's office id.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "required": [ + "email", + "office_id" + ], + "type": "object" + }, + "schemas.user.E911UpdateMessage": { + "properties": { + "address": { + "description": "[single-line only]\n\nLine 1 of the new E911 address.", + "nullable": true, + "type": "string" + }, + "address2": { + "default": "", + "description": "[single-line only]\n\nLine 2 of the new E911 address.", + "nullable": true, + "type": "string" + }, + "city": { + "description": "[single-line only]\n\nCity of the new E911 address.", + "nullable": true, + "type": "string" + }, + "country": { + "description": "Country of the new E911 address.", + "nullable": true, + "type": "string" + }, + "state": { + "description": "[single-line only]\n\nState or Province of the new E911 address.", + "nullable": true, + "type": "string" + }, + "use_validated_option": { + "description": "Whether to use the validated address option from our service.", + "nullable": true, + "type": "boolean" + }, + "zip": { + "description": "[single-line only]\n\nZip of the new E911 address.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "address", + "city", + "country", + "state", + "zip" + ], + "type": "object" + }, + "schemas.user.GroupDetailsProto": { + "properties": { + "do_not_disturb": { + "description": "Whether the user is currently in do-not-disturb mode for this group.", + "nullable": true, + "type": "boolean" + }, + "group_id": { + "description": "The ID of the group.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_type": { + "description": "The group type.", + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "nullable": true, + "type": "string" + }, + "role": { + "description": "The user's role in the group.", + "enum": [ + "admin", + "operator", + "supervisor" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.user.MoveOfficeMessage": { + "properties": { + "office_id": { + "description": "The user's office id. When provided, the user will be moved to this office.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "type": "object" + }, + "schemas.user.PersonaCollection": { + "properties": { + "items": { + "description": "A list of user personas.", + "items": { + "$ref": "#/components/schemas/schemas.user.PersonaProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of personas.", + "type": "object" + }, + "schemas.user.PersonaProto": { + "properties": { + "caller_id": { + "description": "Persona caller ID shown to receivers of calls from this persona.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The user's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "image_url": { + "description": "Persona image URL.", + "nullable": true, + "type": "string" + }, + "name": { + "description": "[single-line only]\n\nPersona name.", + "nullable": true, + "type": "string" + }, + "phone_numbers": { + "description": "List of persona phone numbers.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "type": { + "description": "Persona type.\n\n(corresponds to a target type)", + "nullable": true, + "type": "string" + } + }, + "title": "Persona.", + "type": "object" + }, + "schemas.user.PresenceStatus": { + "properties": { + "message": { + "description": "The presence status message to be updated.", + "nullable": true, + "type": "string" + }, + "provider": { + "description": "The provider requesting the presence status update.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "Predefined templates will be only used for the supported types.\n\nAccepts the following types:\n- `default` -- status message template: \"{provider}: {message}\"\n- `conference` -- status message template: \"On {provider}: in the {message} meeting\"\n\n`provider` and `message` should be chosen with the message template in mind.", + "enum": [ + "conference", + "default" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.user.SetStatusMessage": { + "properties": { + "expiration": { + "description": "The expiration of this status. None for no expiration.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "status_message": { + "description": "The status message for the user.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.user.SetStatusProto": { + "properties": { + "expiration": { + "description": "The expiration of this status. None for no expiration.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "id": { + "description": "The user's id.\n\n('me' can be used if you are using a user level API key)", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "status_message": { + "description": "The status message for the user.", + "nullable": true, + "type": "string" + } + }, + "title": "Set user status.", + "type": "object" + }, + "schemas.user.ToggleDNDMessage": { + "properties": { + "do_not_disturb": { + "description": "Determines if DND is ON or OFF.", + "nullable": true, + "type": "boolean" + }, + "group_id": { + "description": "The ID of the group which the user's DND status will be updated for.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_type": { + "description": "The type of the group which the user's DND status will be updated for.", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + } + }, + "required": [ + "do_not_disturb" + ], + "type": "object" + }, + "schemas.user.ToggleDNDProto": { + "properties": { + "do_not_disturb": { + "description": "Boolean to tell if the user is on DND.", + "nullable": true, + "type": "boolean" + }, + "group_id": { + "description": "The ID of the group which the user's DND status will be updated for.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "group_type": { + "description": "The type of the group which the user's DND status will be updated for.", + "enum": [ + "callcenter", + "department", + "office" + ], + "nullable": true, + "type": "string" + }, + "id": { + "description": "The user's id.\n\n('me' can be used if you are using a user level API key)", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "DND toggle.", + "type": "object" + }, + "schemas.user.UpdateUserMessage": { + "properties": { + "admin_office_ids": { + "description": "The list of admin office IDs.\n\nThis is used to set the user as an office admin for the offices with the provided IDs.", + "items": { + "format": "int64", + "type": "integer" + }, + "nullable": true, + "type": "array" + }, + "emails": { + "description": "The user's emails.\n\nThis can be used to add, remove, or re-order emails. The first email in the list is the user's primary email.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The user's new extension number.\n\nExtensions are optional in Dialpad and turned off by default. If you want extensions please contact support to enable them.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe user's first name.", + "nullable": true, + "type": "string" + }, + "forwarding_numbers": { + "description": "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "international_dialing_enabled": { + "description": "Whether or not the user is enabled to dial internationally.", + "nullable": true, + "type": "boolean" + }, + "is_super_admin": { + "description": "Whether or not the user is a super admin. (company level administrator)", + "nullable": true, + "type": "boolean" + }, + "job_title": { + "description": "[single-line only]\n\nThe user's job title.", + "nullable": true, + "type": "string" + }, + "keep_paid_numbers": { + "default": true, + "description": "Whether or not to keep phone numbers when switching to a support license.\n\nNote: Phone numbers require additional number licenses under a support license.", + "nullable": true, + "type": "boolean" + }, + "last_name": { + "description": "[single-line only]\n\nThe user's last name.", + "nullable": true, + "type": "string" + }, + "license": { + "description": "The user's license type.\n\nChanging this affects billing for the user. For a Sell license, specify the type as `agents`. For a Support license, specify the type as `support`.", + "enum": [ + "admins", + "agents", + "dpde_all", + "dpde_one", + "lite_lines", + "lite_support_agents", + "magenta_lines", + "talk" + ], + "nullable": true, + "type": "string" + }, + "office_id": { + "description": "The user's office id.\n\nIf provided, the user will be moved to this office. For international offices, the user must not have phone numbers assigned. Once the transfer is complete, your admin can add the phone numbers via the user assign number API. Only supported on paid accounts and there must be enough licenses to transfer the user to the destination office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "phone_numbers": { + "description": "A list of the phone number(s) assigned to this user.\n\nThis can be used to re-order or remove numbers. To assign a new number, use the assign number API instead.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "presence_status": { + "$ref": "#/components/schemas/schemas.user.PresenceStatus", + "description": "The presence status can be seen when you hover your mouse over the presence state indicator.\n\nNOTE: this is only used for Highfive and will be deprecated soon.\n\nPresence status will be set to \"{provider}: {message}\" when both are provided. Otherwise,\npresence status will be set to \"{provider}\".\n\n\"type\" is optional and presence status will only include predefined templates when \"type\" is provided. Please refer to the \"type\" parameter to check the supported types.\n\nTo clear the presence status, make an api call with the \"presence_status\" param set to empty or null. ex: `\"presence_status\": {}` or `\"presence_status\": null`\n\nTranslations will be available for the text in predefined templates. Translations for others should be provided.", + "nullable": true, + "type": "object" + }, + "state": { + "description": "The user's state.\n\nThis is used to suspend or re-activate a user.", + "enum": [ + "active", + "suspended" + ], + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.user.UserCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of users.", + "items": { + "$ref": "#/components/schemas/schemas.user.UserProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of users.", + "type": "object" + }, + "schemas.user.UserProto": { + "properties": { + "admin_office_ids": { + "description": "A list of office IDs for which this user has admin privilages.", + "items": { + "format": "int64", + "type": "integer" + }, + "nullable": true, + "type": "array" + }, + "company_id": { + "description": "The id of the user's company.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "country": { + "description": "The country in which the user resides.", + "nullable": true, + "type": "string" + }, + "date_active": { + "description": "The date when the user activated their Dialpad account.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_added": { + "description": "A timestamp indicating when this user was created.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_first_login": { + "description": "A timestamp indicating the first time that this user logged in to Dialpad.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "The user's name, for display purposes.", + "nullable": true, + "type": "string" + }, + "do_not_disturb": { + "description": "A boolean indicating whether the user is currently in \"Do not disturb\" mode.", + "nullable": true, + "type": "boolean" + }, + "duty_status_reason": { + "description": "[single-line only]\n\nA description of this status.", + "nullable": true, + "type": "string" + }, + "duty_status_started": { + "description": "The timestamp, in UTC, when the current on duty status changed.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "emails": { + "description": "A list of email addresses belonging to this user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "extension": { + "description": "The extension that should be associated with this user in the company or office IVR directory.", + "nullable": true, + "type": "string" + }, + "first_name": { + "description": "[single-line only]\n\nThe given name of the user.", + "nullable": true, + "type": "string" + }, + "forwarding_numbers": { + "description": "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "group_details": { + "description": "Details regarding the groups that this user is a member of.", + "items": { + "$ref": "#/components/schemas/schemas.user.GroupDetailsProto", + "type": "object" + }, + "nullable": true, + "type": "array" + }, + "id": { + "description": "The user's id.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "image_url": { + "description": "The url of the user's profile image.", + "nullable": true, + "type": "string" + }, + "international_dialing_enabled": { + "description": "Whether or not the user is enabled to dial internationally.", + "nullable": true, + "type": "boolean" + }, + "is_admin": { + "description": "A boolean indicating whether this user has administor privilages.", + "nullable": true, + "type": "boolean" + }, + "is_available": { + "description": "A boolean indicating whether the user is not currently on a call.", + "nullable": true, + "type": "boolean" + }, + "is_on_duty": { + "description": "A boolean indicating whether this user is currently acting as an operator.", + "nullable": true, + "type": "boolean" + }, + "is_online": { + "description": "A boolean indicating whether the user currently has an active Dialpad device.", + "nullable": true, + "type": "boolean" + }, + "is_super_admin": { + "description": "A boolean indicating whether this user has company-wide administor privilages.", + "nullable": true, + "type": "boolean" + }, + "job_title": { + "description": "[single-line only]\n\nThe user's job title.", + "nullable": true, + "type": "string" + }, + "language": { + "description": "The preferred spoken language of the user.", + "nullable": true, + "type": "string" + }, + "last_name": { + "description": "[single-line only]\n\nThe family name of the user.", + "nullable": true, + "type": "string" + }, + "license": { + "description": "The license type that has been allocated to this user.", + "enum": [ + "admins", + "agents", + "dpde_all", + "dpde_one", + "lite_lines", + "lite_support_agents", + "magenta_lines", + "talk" + ], + "nullable": true, + "type": "string" + }, + "location": { + "description": "[single-line only]\n\nThe self-reported location of the user.", + "nullable": true, + "type": "string" + }, + "muted": { + "description": "A boolean indicating whether the user has muted thier microphone.", + "nullable": true, + "type": "boolean" + }, + "office_id": { + "description": "The ID of the user's office.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "on_duty_started": { + "description": "The timestamp, in UTC, when this operator became available for contact center calls.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "on_duty_status": { + "description": "A description of operator's on duty status.", + "enum": [ + "available", + "busy", + "occupied", + "occupied-end", + "unavailable", + "wrapup", + "wrapup-end" + ], + "nullable": true, + "type": "string" + }, + "phone_numbers": { + "description": "A list of phone numbers belonging to this user.", + "items": { + "type": "string" + }, + "nullable": true, + "type": "array" + }, + "state": { + "description": "The current enablement state of the user.", + "enum": [ + "active", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "nullable": true, + "type": "string" + }, + "status_message": { + "description": "[single-line only]\n\nA message indicating the activity that the user is currently engaged in.", + "nullable": true, + "type": "string" + }, + "timezone": { + "description": "The timezone that this user abides by.", + "nullable": true, + "type": "string" + } + }, + "title": "User.", + "type": "object" + }, + "schemas.userdevice.UserDeviceCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of user devices.", + "items": { + "$ref": "#/components/schemas/schemas.userdevice.UserDeviceProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of user devices.", + "type": "object" + }, + "schemas.userdevice.UserDeviceProto": { + "properties": { + "app_version": { + "description": "The device firmware version, or Dialpad app version.", + "nullable": true, + "type": "string" + }, + "date_created": { + "description": "The time at which this device was created.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_registered": { + "description": "The most recent time at which the device registered with the backend.\n\nDevices register with the backend roughly once per hour, with the exception of mobile devices\n(iphone, ipad, android) for which this field will always be blank.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "date_updated": { + "description": "The most recent time at which the device data was modified.", + "format": "date-time", + "nullable": true, + "type": "string" + }, + "display_name": { + "description": "[single-line only]\n\nThe name of this device.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The ID of the device.", + "nullable": true, + "type": "string" + }, + "phone_number": { + "description": "The phone number associated with this device.", + "nullable": true, + "type": "string" + }, + "type": { + "description": "The device type.", + "enum": [ + "android", + "ata", + "audiocodes", + "c2t", + "ciscompp", + "dect", + "dpmroom", + "grandstream", + "harness", + "iframe_cti_extension", + "iframe_front", + "iframe_hubspot", + "iframe_ms_teams", + "iframe_open_cti", + "iframe_salesforce", + "iframe_service_titan", + "iframe_zendesk", + "ipad", + "iphone", + "mini", + "mitel", + "msteams", + "native", + "obi", + "packaged_app", + "polyandroid", + "polycom", + "proxy", + "public_api", + "salesforce", + "sip", + "tickiot", + "web", + "yealink" + ], + "nullable": true, + "type": "string" + }, + "user_id": { + "description": "The ID of the user who owns the device.", + "format": "int64", + "nullable": true, + "type": "integer" + } + }, + "title": "Dialpad user device.", + "type": "object" + }, + "schemas.webhook.CreateWebhook": { + "properties": { + "hook_url": { + "description": "The webhook's URL. Triggered events will be sent to the url provided here.", + "nullable": true, + "type": "string" + }, + "secret": { + "description": "[single-line only]\n\nWebhook's signature secret that's used to confirm the validity of the request.", + "nullable": true, + "type": "string" + } + }, + "required": [ + "hook_url" + ], + "type": "object" + }, + "schemas.webhook.UpdateWebhook": { + "properties": { + "hook_url": { + "description": "The webhook's URL. Triggered events will be sent to the url provided here.", + "nullable": true, + "type": "string" + }, + "secret": { + "description": "[single-line only]\n\nWebhook's signature secret that's used to confirm the validity of the request.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.webhook.WebhookCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of webhook objects.", + "items": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of webhooks.", + "type": "object" + }, + "schemas.webhook.WebhookProto": { + "properties": { + "hook_url": { + "description": "The webhook's URL. Triggered events will be sent to the url provided here.", + "nullable": true, + "type": "string" + }, + "id": { + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "signature": { + "$ref": "#/components/schemas/schemas.signature.SignatureProto", + "description": "Webhook's signature containing the secret.", + "nullable": true, + "type": "object" + } + }, + "title": "Webhook.", + "type": "object" + }, + "schemas.websocket.CreateWebsocket": { + "properties": { + "secret": { + "description": "[single-line only]\n\nWebsocket's signature secret that's used to confirm the validity of the request.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.websocket.UpdateWebsocket": { + "properties": { + "secret": { + "description": "[single-line only]\n\nWebsocket's signature secret that's used to confirm the validity of the request.", + "nullable": true, + "type": "string" + } + }, + "type": "object" + }, + "schemas.websocket.WebsocketCollection": { + "properties": { + "cursor": { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "nullable": true, + "type": "string" + }, + "items": { + "description": "A list of websocket objects.", + "items": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto", + "type": "object" + }, + "nullable": true, + "type": "array" + } + }, + "title": "Collection of webhooks.", + "type": "object" + }, + "schemas.websocket.WebsocketProto": { + "properties": { + "id": { + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "format": "int64", + "nullable": true, + "type": "integer" + }, + "signature": { + "$ref": "#/components/schemas/schemas.signature.SignatureProto", + "description": "Webhook's signature containing the secret.", + "nullable": true, + "type": "object" + }, + "websocket_url": { + "description": "The websocket's URL. Users need to connect to this url to get event payloads via websocket.", + "nullable": true, + "type": "string" + } + }, + "title": "Websocket.", + "type": "object" + } + }, + "securitySchemes": { + "api_key_in_url": { + "description": "The API key can be put in the URL parameters.\ni.e. ?apikey=\n", + "in": "query", + "name": "apikey", + "type": "apiKey" + }, + "bearer_token": { + "description": "The API key can be put in the Authorization header.\ni.e. Authorization: Bearer ", + "scheme": "bearer", + "type": "http" + } + } + }, + "info": { + "description": "# Introduction\n\nAdmin API v2 for Dialpad.\n\nRequests and responses from the admin API are provided in the JSON format.\n\n# Pagination\n\nList APIs support a limit and cursor parameter.\n\nThe limit defines the number of results to return. For the first request, pass in a desired limit.\nThe API response will contain a cursor field with a special string. Pass this special string into\nthe next request to retrieve the next page.\n\n# Authentication\n\nAll requests are authenticated via an API key in the query parameter or as a bearer token in the\nAuthorization header.\n\nAn API key can be acquired from the Dialpad admin web portal.\n\nNote: If you received your API key from the Dialpad support team rather than the web portal, the\nuser associated with your key must have company administrator permissions.", + "title": "api", + "version": "v2", + "x-logo": { + "altText": "Dialpad", + "url": "https://storage.googleapis.com/dialpad_openapi_specs/logo.png" + } + }, + "openapi": "3.0.0", + "paths": { + "/api/v2/accesscontrolpolicies/{id}/assign": { + "post": { + "deprecated": false, + "description": "Assigns a user to an access control policy.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.assign", + "parameters": [ + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.AssignmentPolicyMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyAssignmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Assign", + "tags": [ + "accesscontrolpolicies" + ] + } + }, + "/api/v2/accesscontrolpolicies": { + "get": { + "deprecated": false, + "description": "Gets all access control policies belonging to the company.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PoliciesCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- List Policies", + "tags": [ + "accesscontrolpolicies" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new custom access control policy.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.CreatePolicyMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Create", + "tags": [ + "accesscontrolpolicies" + ] + } + }, + "/api/v2/accesscontrolpolicies/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a policy by marking the state as deleted, and removing all associated users.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.delete", + "parameters": [ + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Delete", + "tags": [ + "accesscontrolpolicies" + ] + }, + "get": { + "deprecated": false, + "description": "Get a specific access control policy's details.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.get", + "parameters": [ + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Get", + "tags": [ + "accesscontrolpolicies" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the provided fields for an existing access control policy.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.update", + "parameters": [ + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.UpdatePolicyMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Update", + "tags": [ + "accesscontrolpolicies" + ] + } + }, + "/api/v2/accesscontrolpolicies/{id}/assignments": { + "get": { + "deprecated": false, + "description": "Lists all users assigned to this access control policy.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.assignments", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyAssignmentCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- List Assignments", + "tags": [ + "accesscontrolpolicies" + ] + } + }, + "/api/v2/accesscontrolpolicies/{id}/unassign": { + "post": { + "deprecated": false, + "description": "Unassigns one or all target groups associated with the user for an access control policy.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "accesscontrolpolicies.unassign", + "parameters": [ + { + "description": "The access control policy's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.UnassignmentPolicyMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.access_control_policies.PolicyAssignmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Access Control Policies -- Unassign", + "tags": [ + "accesscontrolpolicies" + ] + } + }, + "/api/v2/app/settings": { + "get": { + "deprecated": false, + "description": "Gets the app settings of the OAuth app that is associated with the API key for the target, if target_type and target_id are provided. Otherwise, will return the app settings for the company.\n\nRate limit: 1200 per minute.", + "operationId": "app_settings.get", + "parameters": [ + { + "description": "The target's id.", + "in": "query", + "name": "target_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The target's type.", + "in": "query", + "name": "target_type", + "required": false, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "enabled": true, + "is_preferred_service": true, + "settings": { + "bar": "high", + "call_logging_enabled": true, + "call_recording_logging_enabled": false, + "enable_foo": true, + "foo": "bar1", + "foo2": 2, + "log_auto_external_transferred_call": true, + "log_call_recordings": false, + "sf_case_owner_sms": false, + "sms_logging_enabled": false, + "voicemail_logging_enabled": false + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.app.setting.AppSettingProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "App Settings -- GET", + "tags": [ + "app" + ] + } + }, + "/api/v2/blockednumbers/add": { + "post": { + "deprecated": false, + "description": "Blocks the specified numbers company-wide.\n\nRate limit: 1200 per minute.", + "operationId": "blockednumbers.add", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.blocked_number.AddBlockedNumbersProto", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Blocked Number -- Add", + "tags": [ + "blockednumbers" + ] + } + }, + "/api/v2/blockednumbers/{number}": { + "get": { + "deprecated": false, + "description": "Gets the specified blocked number.\n\nRate limit: 1200 per minute.", + "operationId": "blockednumbers.get", + "parameters": [ + { + "description": "A phone number (e164 format).", + "in": "path", + "name": "number", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.blocked_number.BlockedNumber" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Blocked Number -- Get", + "tags": [ + "blockednumbers" + ] + } + }, + "/api/v2/blockednumbers/remove": { + "post": { + "deprecated": false, + "description": "Unblocks the specified numbers company-wide.\n\nRate limit: 1200 per minute.", + "operationId": "blockednumbers.remove", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.blocked_number.RemoveBlockedNumbersProto", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Blocked Number -- Remove", + "tags": [ + "blockednumbers" + ] + } + }, + "/api/v2/blockednumbers": { + "get": { + "deprecated": false, + "description": "Lists all numbers that have been blocked via the API.\n\nRate limit: 1200 per minute.", + "operationId": "blockednumbers.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.blocked_number.BlockedNumberCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Blocked Numbers -- List", + "tags": [ + "blockednumbers" + ] + } + }, + "/api/v2/call/{id}/participants/add": { + "post": { + "deprecated": false, + "description": "Adds another participant to a call. Valid methods to add are by phone or by target. Targets require to have a primary phone Added on Nov 11, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call.participants.add", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.AddParticipantMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.RingCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Add Participant", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/{id}": { + "get": { + "deprecated": false, + "description": "Get Call status and other information. Added on May 25, 2021 for API v2.\n\nRate limit: 10 per minute.", + "operationId": "call.get_call_info", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_id": "1002", + "contact": { + "email": "", + "id": "1001", + "name": "(415) 555-7777", + "phone": "+14155557777", + "type": "local" + }, + "custom_data": "{\"service_titan\": \"my awesome custom data\"}", + "date_connected": "1356998400000", + "date_rang": "1356998400000", + "date_started": "1356998400000", + "direction": "inbound", + "entry_point_call_id": "1000", + "entry_point_target": { + "email": "", + "id": "124", + "name": "\u30d5\u30a1\u30fc\u30b8\u30fc\u3000\u30dc\u30fc\u30eb, Inc", + "phone": "+14155551000", + "type": "office" + }, + "event_timestamp": "1356998400000", + "external_number": "+14155557777", + "group_id": "Office:124", + "internal_number": "+14155551001", + "is_transferred": false, + "proxy_target": {}, + "state": "connected", + "target": { + "email": "bot@fuzz-ball.com", + "id": "2", + "name": "\u30c6\u00c9st Bot", + "phone": "+14155551001", + "type": "user" + }, + "was_recorded": false + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.CallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Get", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/initiate_ivr_call": { + "post": { + "deprecated": false, + "description": "Initiates an outbound call to ring an IVR Workflow.\n\nAdded on Aug 14, 2023 for API v2.\n\nRate limit: 10 per minute per IVR.\n\nRate limit: 1200 per minute.", + "operationId": "call.initiate_ivr_call", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.OutboundIVRMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.InitiatedIVRCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Initiate IVR Call", + "tags": [ + "call" + ] + } + }, + "/api/v2/call": { + "get": { + "deprecated": false, + "description": "Provides a paginated list of calls matching the specified filter parameters in reverse-chronological order by call start time (i.e. recent calls first)\n\nNote: This API will only include calls that have already concluded.\n\nAdded on May 27, 2024 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``calls:list``\n\nRate limit: 1200 per minute.", + "operationId": "call.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Only includes calls that started more recently than the specified timestamp.\n(UTC ms-since-epoch timestamp)", + "in": "query", + "name": "started_after", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "Only includes calls that started prior to the specified timestamp.\n(UTC ms-since-epoch timestamp)", + "in": "query", + "name": "started_before", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The ID of a target to filter against.", + "in": "query", + "name": "target_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The target type associated with the target ID.", + "in": "query", + "name": "target_type", + "required": false, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.CallCollection" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "calls:list" + ] + }, + { + "bearer_token": [ + "calls:list" + ] + } + ], + "summary": "Call -- List", + "tags": [ + "call" + ] + }, + "post": { + "deprecated": false, + "description": "Initiates an outbound call to ring all devices (or a single specified device).\n\nAdded on Feb 20, 2020 for API v2.\n\nRate limit: 5 per minute.", + "operationId": "call.call", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.RingCallMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.RingCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Initiate via Ring", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/{id}/transfer": { + "post": { + "deprecated": false, + "description": "Transfers call to another recipient. Added on Sep 25, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call.transfer_call", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.TransferCallMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_id": "1007", + "transferred_to_number": "+14155551003", + "transferred_to_state": "hold" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.TransferredCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Transfer", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/{id}/unpark": { + "post": { + "deprecated": false, + "description": "Unparks call from Office mainline. Added on Nov 11, 2024 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call.unpark", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.UnparkCallMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.RingCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Unpark", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/{id}/actions/hangup": { + "put": { + "deprecated": false, + "description": "Hangs up the call. Added on Oct 25, 2024 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call.actions.hangup", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Call Actions -- Hang up", + "tags": [ + "call" + ] + } + }, + "/api/v2/call/{id}/labels": { + "put": { + "deprecated": false, + "description": "Set Labels for a determined call id.\n\nAdded on Nov 15, 2022 for API v2.\n\nRate limit: 250 per minute.", + "operationId": "call.put_call_labels", + "parameters": [ + { + "description": "The call's id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.AddCallLabelsMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.CallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Label -- Set", + "tags": [ + "call" + ] + } + }, + "/api/v2/callback": { + "post": { + "deprecated": false, + "description": "Requests a call back to a given number by an operator in a given call center. The call back is added to the queue for the call center like a regular call, and a call is initiated when the next operator becomes available. This API respects all existing call center settings,\ne.g. business / holiday hours and queue settings. This API currently does not allow international call backs. Duplicate call backs for a given number and call center are not allowed. Specific error messages will be provided in case of failure.\n\nAdded on Dec 9, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call.callback", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.CallbackMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "position": "1" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.CallbackProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Back -- Enqueue", + "tags": [ + "callback" + ] + } + }, + "/api/v2/callback/validate": { + "post": { + "deprecated": false, + "description": "Performs a dry-run of creating a callback request, without adding it to the call center queue.\n\nThis performs the same validation logic as when actually enqueuing a callback request, allowing early identification of problems which would prevent a successful callback request.\n\nRate limit: 1200 per minute.", + "operationId": "call.validate_callback", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.CallbackMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "success": true + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.ValidateCallbackProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Back -- Validate", + "tags": [ + "callback" + ] + } + }, + "/api/v2/callcenters": { + "get": { + "deprecated": false, + "description": "Gets all the call centers for the company. Added on Feb 3, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.listall", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "search call center by office.", + "in": "query", + "name": "office_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "search call centers by name or search by the substring of the name. If input example is 'Cool', output example can be a list of call centers whose name contains the string\n'Cool' - ['Cool call center 1', 'Cool call center 2049']", + "in": "query", + "name": "name_search", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "advanced_settings": { + "auto_call_recording": { + "call_recording_inbound": false, + "call_recording_outbound": false + }, + "max_wrap_up_seconds": "0" + }, + "alerts": { + "cc_service_level": "95", + "cc_service_level_seconds": "60" + }, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "hold_queue": { + "announce_position": true, + "announcement_interval_seconds": "120", + "max_hold_count": "50", + "max_hold_seconds": "900", + "queue_callback_dtmf": "9", + "queue_callback_threshold": "5", + "queue_escape_dtmf": "*" + }, + "hours_on": false, + "id": "1000", + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "call center 2049", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- List", + "tags": [ + "callcenters" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new call center.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.CreateCallCenterMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "advanced_settings": { + "auto_call_recording": { + "call_recording_inbound": true, + "call_recording_outbound": true + }, + "max_wrap_up_seconds": "5" + }, + "alerts": { + "cc_service_level": "96", + "cc_service_level_seconds": "59" + }, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "group_description": "a cool test cc.", + "hold_queue": { + "announce_position": false, + "announcement_interval_seconds": "130", + "max_hold_count": "55", + "max_hold_seconds": "930", + "queue_callback_dtmf": "8", + "queue_callback_threshold": "4", + "queue_escape_dtmf": "*" + }, + "hours_on": true, + "id": "1003", + "monday_hours": [ + "09:00", + "12:00", + "14:00", + "17:00" + ], + "name": "call center 2046", + "no_operators_action": "voicemail", + "office_id": "124", + "ring_seconds": "45", + "routing_options": { + "closed": { + "action": "person", + "action_target_id": "2", + "action_target_type": "user", + "dtmf": [ + { + "input": "0", + "options": { + "action": "voicemail" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "fixedorder", + "try_dial_operators": true + }, + "open": { + "action": "menu", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + }, + { + "input": "2", + "options": { + "action": "voicemail", + "action_target_id": "2", + "action_target_type": "user" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + } + }, + "saturday_hours": [ + "08:00", + "18:00" + ], + "state": "active", + "sunday_hours": [ + "08:00", + "18:00" + ], + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": false, + "auto_start": false + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- Create", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/callcenters/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a call center by id.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.delete", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- Delete", + "tags": [ + "callcenters" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a call center by id. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.get", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "advanced_settings": { + "auto_call_recording": { + "call_recording_inbound": false, + "call_recording_outbound": false + }, + "max_wrap_up_seconds": "0" + }, + "alerts": { + "cc_service_level": "95", + "cc_service_level_seconds": "60" + }, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "hold_queue": { + "announce_position": true, + "announcement_interval_seconds": "120", + "max_hold_count": "50", + "max_hold_seconds": "900", + "queue_callback_dtmf": "9", + "queue_callback_threshold": "5", + "queue_escape_dtmf": "*" + }, + "hours_on": false, + "id": "1000", + "name": "call center 2049", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": false + }, + "open": { + "action": "bridge_target", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": true + } + }, + "state": "active", + "timezone": "US/Pacific", + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- Get", + "tags": [ + "callcenters" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a call center by id.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.update", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UpdateCallCenterMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "advanced_settings": { + "auto_call_recording": { + "call_recording_inbound": true, + "call_recording_outbound": true + }, + "max_wrap_up_seconds": "5" + }, + "alerts": { + "cc_service_level": "96", + "cc_service_level_seconds": "59" + }, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "group_description": "a cool test cc.", + "hold_queue": { + "announce_position": false, + "announcement_interval_seconds": "130", + "max_hold_count": "55", + "max_hold_seconds": "930", + "queue_callback_dtmf": "8", + "queue_callback_threshold": "4", + "queue_escape_dtmf": "*" + }, + "hours_on": true, + "id": "1000", + "monday_hours": [ + "09:00", + "12:00", + "14:00", + "17:00" + ], + "name": "call center 2046", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "45", + "routing_options": { + "closed": { + "action": "person", + "action_target_id": "2", + "action_target_type": "user", + "dtmf": [ + { + "input": "0", + "options": { + "action": "voicemail" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "fixedorder", + "try_dial_operators": true + }, + "open": { + "action": "menu", + "dtmf": [ + { + "input": "0", + "options": { + "action": "voicemail", + "action_target_id": "2", + "action_target_type": "user" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": false, + "auto_start": false + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- Update", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/callcenters/{id}/status": { + "get": { + "deprecated": false, + "description": "Gets live status information on the corresponding Call Center.\n\nAdded on August 7, 2023 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.status", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterStatusProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- Status", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/callcenters/operators/{id}/dutystatus": { + "get": { + "deprecated": false, + "description": "Gets the operator's on duty status and reason.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.get.dutystatus", + "parameters": [ + { + "description": "The operator's user id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "duty_status_reason": "Lunch Time", + "duty_status_started": "1356998400000", + "on_duty": false, + "on_duty_started": "1356998400000", + "on_duty_status": "unavailable", + "user_id": "2" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorDutyStatusProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Get Duty Status", + "tags": [ + "callcenters" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the operator's duty status for all call centers which user belongs to.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.dutystatus", + "parameters": [ + { + "description": "The operator's user id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UpdateOperatorDutyStatusMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "duty_status_reason": "Lunch Time", + "duty_status_started": "1356998400000", + "on_duty": false, + "on_duty_started": "1356998400000", + "on_duty_status": "unavailable", + "user_id": "2" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorDutyStatusProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Update Duty Status", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/callcenters/{call_center_id}/operators/{user_id}/skill": { + "get": { + "deprecated": false, + "description": "Gets the skill level of an operator within a call center.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.get.skilllevel", + "parameters": [ + { + "description": "The call center's ID", + "in": "path", + "name": "call_center_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The operator's ID", + "in": "path", + "name": "user_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorSkillLevelProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Get Skill Level", + "tags": [ + "callcenters" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the skill level of an operator within a call center.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.skilllevel", + "parameters": [ + { + "description": "The call center's ID", + "in": "path", + "name": "call_center_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "The operator's ID", + "in": "path", + "name": "user_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UpdateOperatorSkillLevelMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorSkillLevelProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Update Skill Level", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/callcenters/{id}/operators": { + "delete": { + "deprecated": false, + "description": "Removes an operator from a call center.\n\nNote: This API will not change or release any licenses.\n\nAdded on October 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.delete", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.RemoveCallCenterOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "2", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIRCxILVXNlclByb2ZpbGUYAgw.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_on_duty": false, + "name": "\u30c6\u00c9st Bot", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Remove", + "tags": [ + "callcenters" + ] + }, + "get": { + "deprecated": false, + "description": "Gets operators for a call center. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.get", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "users": [ + { + "admin_office_ids": [ + "124" + ], + "company_id": "123", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "do_not_disturb": false, + "emails": [ + "bot@fuzz-ball.com" + ], + "extension": "20000", + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "1000", + "group_type": "callcenter", + "role": "admin" + } + ], + "id": "2", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIRCxILVXNlclByb2ZpbGUYAgw.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "124", + "on_duty_status": "unavailable", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operators -- List", + "tags": [ + "callcenters" + ] + }, + "post": { + "deprecated": false, + "description": "Adds an operator to a call center.\n\n> Warning\n>\n> This API may result in the usage of call center licenses if required and available. If the licenses are required and not available the operation will fail. Licenses are required when adding an operator that does not have a call center license.\n\nAdded on October 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.operators.post", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.AddCallCenterOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "2", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIRCxILVXNlclByb2ZpbGUYAgw.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_on_duty": false, + "name": "\u30c6\u00c9st Bot", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Add", + "tags": [ + "callcenters" + ] + } + }, + "/api/v2/calllabels": { + "get": { + "deprecated": false, + "description": "Gets all labels for a determined company.\n\nAdded on Nov 15, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "calllabel.list", + "parameters": [ + { + "description": "The maximum number of results to return.", + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_label.CompanyCallLabels" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Label -- List", + "tags": [ + "calllabels" + ] + } + }, + "/api/v2/callreviewsharelink": { + "post": { + "deprecated": false, + "description": "Create a call review share link by call id.\n\nAdded on Sep 21, 2022 for API v2.\n\nRate limit: 250 per minute.", + "operationId": "call_review_share_link.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.CreateCallReviewShareLink", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.CallReviewShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Review Sharelink -- Create", + "tags": [ + "callreviewsharelink" + ] + } + }, + "/api/v2/callreviewsharelink/{id}": { + "delete": { + "deprecated": false, + "description": "Delete a call review share link by id.\n\nAdded on Sep 21, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call_review_share_link.delete", + "parameters": [ + { + "description": "The share link's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.CallReviewShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Review Sharelink -- Delete", + "tags": [ + "callreviewsharelink" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a call review share link by call id.\n\nAdded on Sep 21, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "call_review_share_link.get", + "parameters": [ + { + "description": "The share link's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.CallReviewShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Review Sharelink -- Get", + "tags": [ + "callreviewsharelink" + ] + }, + "put": { + "deprecated": false, + "description": "Update a call review share link by id.\n\nAdded on Sep 21, 2022 for API v2.\n\nRate limit: 250 per minute.", + "operationId": "call_review_share_link.update", + "parameters": [ + { + "description": "The share link's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.UpdateCallReviewShareLink", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_review_share_link.CallReviewShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Review Sharelink -- Update", + "tags": [ + "callreviewsharelink" + ] + } + }, + "/api/v2/callrouters": { + "get": { + "deprecated": false, + "description": "Lists all of the API call routers for a given company or office.\n\nRate limit: 1200 per minute.", + "operationId": "callrouters.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The office's id.", + "in": "query", + "name": "office_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "default_target_id": "5835806903417907", + "default_target_type": "user", + "enabled": true, + "id": "5165402724715258", + "name": "Test Router", + "office_id": "6286560707051431", + "routing_url": "https://example.ca/routingapi", + "signature": {} + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_router.ApiCallRouterCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Router -- List", + "tags": [ + "callrouters" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new API-based call router.\n\nRate limit: 1200 per minute.", + "operationId": "callrouters.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_router.CreateApiCallRouterMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "default_target_id": "5835806903417907", + "default_target_type": "user", + "enabled": true, + "id": "5165402724715258", + "name": "Test Router", + "office_id": "6286560707051431", + "routing_url": "https://example.ca/routingapi", + "signature": {} + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_router.ApiCallRouterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Router -- Create", + "tags": [ + "callrouters" + ] + } + }, + "/api/v2/callrouters/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes the API call router with the given ID.\n\nRate limit: 1200 per minute.", + "operationId": "callrouters.delete", + "parameters": [ + { + "description": "The API call router's ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Call Router -- Delete", + "tags": [ + "callrouters" + ] + }, + "get": { + "deprecated": false, + "description": "Gets the API call router with the given ID.\n\nRate limit: 1200 per minute.", + "operationId": "callrouters.get", + "parameters": [ + { + "description": "The API call router's ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "default_target_id": "5835806903417907", + "default_target_type": "user", + "enabled": true, + "id": "5165402724715258", + "name": "Test Router", + "office_id": "6422934115149452", + "routing_url": "https://example.ca/routingapi", + "signature": {} + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_router.ApiCallRouterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Router -- Get", + "tags": [ + "callrouters" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the API call router with the given ID.\n\nRate limit: 1 per 5 minute.", + "operationId": "callrouters.update", + "parameters": [ + { + "description": "The API call router's ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_router.UpdateApiCallRouterMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "default_target_id": "5835806903417907", + "default_target_type": "user", + "enabled": true, + "id": "5165402724715258", + "name": "Test Router", + "office_id": "6422934115149452", + "routing_url": "https://example.ca/routingapi", + "signature": {} + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_router.ApiCallRouterProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Router -- Update", + "tags": [ + "callrouters" + ] + } + }, + "/api/v2/callrouters/{id}/assign_number": { + "post": { + "deprecated": false, + "description": "Assigns a number to a callrouter. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_call_router_number.post", + "parameters": [ + { + "description": "The API call router's ID", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551005", + "office_id": "132", + "status": "call_router", + "target_id": "1002", + "target_type": "callrouter", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Assign", + "tags": [ + "callrouters" + ] + } + }, + "/api/v2/channels/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a channel by id.\n\nAdded on May 11, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.delete", + "parameters": [ + { + "description": "The channel id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Channel -- Delete", + "tags": [ + "channels" + ] + }, + "get": { + "deprecated": false, + "description": "Get channel by id\n\nAdded on May 11, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.get", + "parameters": [ + { + "description": "The channel id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.channel.ChannelProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Channel -- Get", + "tags": [ + "channels" + ] + } + }, + "/api/v2/channels": { + "get": { + "deprecated": false, + "description": "Lists all channels in the company.\n\nAdded on May 11, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The state of the channel.", + "in": "query", + "name": "state", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.channel.ChannelCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Channel -- List", + "tags": [ + "channels" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new channel.\n\nAdded on May 11, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.post", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.channel.CreateChannelMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.channel.ChannelProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Channel -- Create", + "tags": [ + "channels" + ] + } + }, + "/api/v2/channels/{id}/members": { + "delete": { + "deprecated": false, + "description": "Removes a member from a channel.\n\nAdded on May 12, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.members.delete", + "parameters": [ + { + "description": "The channel's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.member_channel.RemoveChannelMemberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Member -- Remove", + "tags": [ + "channels" + ] + }, + "get": { + "deprecated": false, + "description": "List all the members from a channel\n\nAdded on May 11, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.members.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The channel id", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.member_channel.MembersCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Members -- List", + "tags": [ + "channels" + ] + }, + "post": { + "deprecated": false, + "description": "Adds an user to a channel.\n\nAdded on May 12, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "channels.members.post", + "parameters": [ + { + "description": "The channel's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.member_channel.AddChannelMemberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.member_channel.MembersProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Member -- Add", + "tags": [ + "channels" + ] + } + }, + "/api/v2/coachingteams/{id}/members": { + "get": { + "deprecated": false, + "description": "Get a list of members of a coaching team. Added on Jul 30th, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "coaching_team.members.get", + "parameters": [ + { + "description": "Id of the coaching team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "cursor": "", + "items": [ + { + "company_id": "123", + "country": "us", + "date_added": "2013-01-01T00:00:00", + "do_not_disturb": false, + "emails": [ + "test-email@test.com" + ], + "first_name": "\u30c6\u00c9st", + "id": "1130", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXISCxILVXNlclByb2ZpbGUY6ggM.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_admin": false, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": false, + "language": "en", + "last_name": "Bot", + "license": "talk", + "muted": false, + "office_id": "124", + "phone_numbers": [ + "+14155551006" + ], + "role": "trainee", + "state": "active", + "timezone": "US/Pacific" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamMemberCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Coaching Team -- List Members", + "tags": [ + "coachingteams" + ] + }, + "post": { + "deprecated": false, + "description": "Add a user to the specified coaching team as trainee or coach.\n\nAdded on July 5th, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "coaching_team.members.add", + "parameters": [ + { + "description": "Id of the coaching team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamMemberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "date_added": "2013-01-01T00:00:00", + "do_not_disturb": false, + "emails": [ + "test-email@test.com" + ], + "first_name": "\u30c6\u00c9st", + "id": "1130", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXISCxILVXNlclByb2ZpbGUY6ggM.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_admin": false, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": false, + "language": "en", + "last_name": "Bot", + "license": "talk", + "muted": false, + "office_id": "124", + "phone_numbers": [ + "+14155551006" + ], + "role": "trainee", + "state": "active", + "timezone": "US/Pacific" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamMemberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Coaching Team -- Add Member", + "tags": [ + "coachingteams" + ] + } + }, + "/api/v2/coachingteams/{id}": { + "get": { + "deprecated": false, + "description": "Get details of a specified coaching team. Added on Jul 30th, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "coaching_team.get", + "parameters": [ + { + "description": "Id of the coaching team", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "allow_trainee_eavesdrop": true, + "company_id": "123", + "country": "us", + "id": "1130", + "name": "team_name2", + "office_id": "124", + "state": "active" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Coaching Team -- Get", + "tags": [ + "coachingteams" + ] + } + }, + "/api/v2/coachingteams": { + "get": { + "deprecated": false, + "description": "Get a list of all coaching teams in the company. Added on Feb 3rd, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "coaching_team.listall", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "allow_trainee_eavesdrop": true, + "company_id": "123", + "country": "us", + "id": "1001", + "name": "team_name", + "office_id": "124", + "state": "active" + }, + { + "allow_trainee_eavesdrop": true, + "company_id": "123", + "country": "us", + "id": "1130", + "name": "team_name2", + "office_id": "124", + "state": "active" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Coaching Team -- List", + "tags": [ + "coachingteams" + ] + } + }, + "/api/v2/company": { + "get": { + "deprecated": false, + "description": "Gets company information.\n\nAdded on Feb 21, 2019 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "company.get", + "parameters": [], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "account_type": "standard", + "country": "us", + "domain": "fuzz-ball.com", + "id": "123", + "name": "\u30d5\u30a1\u30fc\u30b8\u30fc\u3000\u30dc\u30fc\u30eb, Inc", + "office_count": "1" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.company.CompanyProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Company -- Get", + "tags": [ + "company" + ] + } + }, + "/api/v2/company/{id}/smsoptout": { + "get": { + "deprecated": false, + "description": "\n\nRequires a company admin API key.\n\nRate limit: 250 per minute.", + "operationId": "company.sms_opt_out", + "parameters": [ + { + "description": "ID of the requested company. This is required and must be specified in the URL path. The value must match the id from the company linked to the API key.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Optional company A2P campaign entity id to filter results by. Note, if set,\nthen the parameter 'opt_out_state' must be also set to the value 'opted_out'.", + "in": "query", + "name": "a2p_campaign_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "Optional token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Required opt-out state to filter results by. Only results matching this state will be returned.", + "in": "query", + "name": "opt_out_state", + "required": true, + "schema": { + "enum": [ + "opted_back_in", + "opted_out" + ], + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.sms_opt_out.SmsOptOutListProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Company -- Get SMS Opt-out List", + "tags": [ + "company" + ] + } + }, + "/api/v2/conference/rooms": { + "get": { + "deprecated": false, + "description": "Lists all conference rooms.\n\nRequires scope: ``conference:read``\n\nRate limit: 1200 per minute.", + "operationId": "conference-rooms.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.uberconference.room.RoomCollection" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "conference:read" + ] + }, + { + "bearer_token": [ + "conference:read" + ] + } + ], + "summary": "Meeting Room -- List", + "tags": [ + "conference" + ] + } + }, + "/api/v2/conference/meetings": { + "get": { + "deprecated": false, + "description": "Lists summaries of meetings that have occured in the specified meeting room.\n\nRequires scope: ``conference:read``\n\nRate limit: 1200 per minute.", + "operationId": "conference-meetings.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The meeting room's ID.", + "in": "query", + "name": "room_id", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.uberconference.meeting.MeetingSummaryCollection" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "conference:read" + ] + }, + { + "bearer_token": [ + "conference:read" + ] + } + ], + "summary": "Meeting Summary -- List", + "tags": [ + "conference" + ] + } + }, + "/api/v2/contacts/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a contact by id. Added on Mar 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "contacts.delete", + "parameters": [ + { + "description": "The contact's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- Delete", + "tags": [ + "contacts" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a contact by id. Currently, only contacts of type shared and local can be retrieved by this API.\n\nAdded on Mar 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "contacts.get", + "parameters": [ + { + "description": "The contact's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- Get", + "tags": [ + "contacts" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the provided fields for an existing contact. Added on Mar 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "contacts.update", + "parameters": [ + { + "description": "The contact's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.UpdateContactMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- Update", + "tags": [ + "contacts" + ] + } + }, + "/api/v2/contacts": { + "get": { + "deprecated": false, + "description": "Gets company shared contacts, or user's local contacts if owner_id is provided.\n\nAdded on Mar 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "contacts.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "If set to True company local contacts will be included. default False.", + "in": "query", + "name": "include_local", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "The id of the user who owns the contact.", + "in": "query", + "name": "owner_id", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- List", + "tags": [ + "contacts" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new contact. Added on Mar 2, 2020 for API v2.\n\nRate limit: 100 per minute.", + "operationId": "contacts.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.CreateContactMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- Create", + "tags": [ + "contacts" + ] + }, + "put": { + "deprecated": false, + "description": "Creates a new shared contact with uid. Added on Jun 11, 2020 for API v2.\n\nRate limit: 100 per minute.", + "operationId": "contacts.create_with_uid", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.CreateContactMessageWithUid", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact.ContactProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact -- Create or Update", + "tags": [ + "contacts" + ] + } + }, + "/api/v2/customivrs/{target_type}/{target_id}/{ivr_type}": { + "delete": { + "deprecated": false, + "description": "Delete and un-assign an Ivr from a target.\n\nRate limit: 1200 per minute.", + "operationId": "ivr.delete", + "parameters": [ + { + "description": "Target's type. of the custom ivr to be updated.", + "in": "path", + "name": "target_type", + "required": true, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + }, + { + "description": "The id of the target.", + "in": "path", + "name": "target_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "Type of ivr you want to update.", + "in": "path", + "name": "ivr_type", + "required": true, + "schema": { + "enum": [ + "ASK_FIRST_OPERATOR_NOT_AVAILABLE", + "AUTO_RECORDING", + "CALLAI_AUTO_RECORDING", + "CG_AUTO_RECORDING", + "CLOSED", + "CLOSED_DEPARTMENT_INTRO", + "CLOSED_MENU", + "CLOSED_MENU_OPTION", + "CSAT_INTRO", + "CSAT_OUTRO", + "CSAT_PREAMBLE", + "CSAT_QUESTION", + "DEPARTMENT_INTRO", + "GREETING", + "HOLD_AGENT_READY", + "HOLD_APPREC", + "HOLD_CALLBACK_ACCEPT", + "HOLD_CALLBACK_ACCEPTED", + "HOLD_CALLBACK_CONFIRM", + "HOLD_CALLBACK_CONFIRM_NUMBER", + "HOLD_CALLBACK_DIFFERENT_NUMBER", + "HOLD_CALLBACK_DIRECT", + "HOLD_CALLBACK_FULFILLED", + "HOLD_CALLBACK_INVALID_NUMBER", + "HOLD_CALLBACK_KEYPAD", + "HOLD_CALLBACK_REJECT", + "HOLD_CALLBACK_REJECTED", + "HOLD_CALLBACK_REQUEST", + "HOLD_CALLBACK_REQUESTED", + "HOLD_CALLBACK_SAME_NUMBER", + "HOLD_CALLBACK_TRY_AGAIN", + "HOLD_CALLBACK_UNDIALABLE", + "HOLD_ESCAPE_VM_EIGHT", + "HOLD_ESCAPE_VM_FIVE", + "HOLD_ESCAPE_VM_FOUR", + "HOLD_ESCAPE_VM_NINE", + "HOLD_ESCAPE_VM_ONE", + "HOLD_ESCAPE_VM_POUND", + "HOLD_ESCAPE_VM_SEVEN", + "HOLD_ESCAPE_VM_SIX", + "HOLD_ESCAPE_VM_STAR", + "HOLD_ESCAPE_VM_TEN", + "HOLD_ESCAPE_VM_THREE", + "HOLD_ESCAPE_VM_TWO", + "HOLD_ESCAPE_VM_ZERO", + "HOLD_INTERRUPT", + "HOLD_INTRO", + "HOLD_MUSIC", + "HOLD_POSITION_EIGHT", + "HOLD_POSITION_FIVE", + "HOLD_POSITION_FOUR", + "HOLD_POSITION_MORE", + "HOLD_POSITION_NINE", + "HOLD_POSITION_ONE", + "HOLD_POSITION_SEVEN", + "HOLD_POSITION_SIX", + "HOLD_POSITION_TEN", + "HOLD_POSITION_THREE", + "HOLD_POSITION_TWO", + "HOLD_POSITION_ZERO", + "HOLD_WAIT", + "MENU", + "MENU_OPTION", + "NEXT_TARGET", + "VM_DROP_MESSAGE", + "VM_UNAVAILABLE", + "VM_UNAVAILABLE_CLOSED" + ], + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.UpdateCustomIvrMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "date_added": "1356998400000", + "id": "1002", + "name": "fake", + "selected": false + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrDetailsProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Custom IVR -- Delete", + "tags": [ + "customivrs" + ] + }, + "patch": { + "deprecated": false, + "description": "Sets an existing Ivr for a target.\n\nAdded on July 27, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "ivr.update", + "parameters": [ + { + "description": "Target's type.", + "in": "path", + "name": "target_type", + "required": true, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + }, + { + "description": "The target's id.", + "in": "path", + "name": "target_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "Type of ivr you want to update", + "in": "path", + "name": "ivr_type", + "required": true, + "schema": { + "enum": [ + "ASK_FIRST_OPERATOR_NOT_AVAILABLE", + "AUTO_RECORDING", + "CALLAI_AUTO_RECORDING", + "CG_AUTO_RECORDING", + "CLOSED", + "CLOSED_DEPARTMENT_INTRO", + "CLOSED_MENU", + "CLOSED_MENU_OPTION", + "CSAT_INTRO", + "CSAT_OUTRO", + "CSAT_PREAMBLE", + "CSAT_QUESTION", + "DEPARTMENT_INTRO", + "GREETING", + "HOLD_AGENT_READY", + "HOLD_APPREC", + "HOLD_CALLBACK_ACCEPT", + "HOLD_CALLBACK_ACCEPTED", + "HOLD_CALLBACK_CONFIRM", + "HOLD_CALLBACK_CONFIRM_NUMBER", + "HOLD_CALLBACK_DIFFERENT_NUMBER", + "HOLD_CALLBACK_DIRECT", + "HOLD_CALLBACK_FULFILLED", + "HOLD_CALLBACK_INVALID_NUMBER", + "HOLD_CALLBACK_KEYPAD", + "HOLD_CALLBACK_REJECT", + "HOLD_CALLBACK_REJECTED", + "HOLD_CALLBACK_REQUEST", + "HOLD_CALLBACK_REQUESTED", + "HOLD_CALLBACK_SAME_NUMBER", + "HOLD_CALLBACK_TRY_AGAIN", + "HOLD_CALLBACK_UNDIALABLE", + "HOLD_ESCAPE_VM_EIGHT", + "HOLD_ESCAPE_VM_FIVE", + "HOLD_ESCAPE_VM_FOUR", + "HOLD_ESCAPE_VM_NINE", + "HOLD_ESCAPE_VM_ONE", + "HOLD_ESCAPE_VM_POUND", + "HOLD_ESCAPE_VM_SEVEN", + "HOLD_ESCAPE_VM_SIX", + "HOLD_ESCAPE_VM_STAR", + "HOLD_ESCAPE_VM_TEN", + "HOLD_ESCAPE_VM_THREE", + "HOLD_ESCAPE_VM_TWO", + "HOLD_ESCAPE_VM_ZERO", + "HOLD_INTERRUPT", + "HOLD_INTRO", + "HOLD_MUSIC", + "HOLD_POSITION_EIGHT", + "HOLD_POSITION_FIVE", + "HOLD_POSITION_FOUR", + "HOLD_POSITION_MORE", + "HOLD_POSITION_NINE", + "HOLD_POSITION_ONE", + "HOLD_POSITION_SEVEN", + "HOLD_POSITION_SIX", + "HOLD_POSITION_TEN", + "HOLD_POSITION_THREE", + "HOLD_POSITION_TWO", + "HOLD_POSITION_ZERO", + "HOLD_WAIT", + "MENU", + "MENU_OPTION", + "NEXT_TARGET", + "VM_DROP_MESSAGE", + "VM_UNAVAILABLE", + "VM_UNAVAILABLE_CLOSED" + ], + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.UpdateCustomIvrMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "ivr_type": "DEPARTMENT_INTRO", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1001", + "name": "fake", + "selected": false + }, + { + "date_added": "1356998400000", + "id": "1002", + "name": "test_mp3", + "selected": false + }, + { + "date_added": "1356998400000", + "id": "1003", + "name": "test_mp3", + "selected": true + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Custom IVR -- Assign", + "tags": [ + "customivrs" + ] + } + }, + "/api/v2/customivrs": { + "get": { + "deprecated": false, + "description": "Gets all the custom IVRs for a target.\n\nAdded on July 14, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "custom_ivrs.get", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Target's type.", + "in": "query", + "name": "target_type", + "required": true, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + }, + { + "description": "The target's id.", + "in": "query", + "name": "target_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "cursor": "", + "items": [ + { + "ivr_type": "HOLD_APPREC", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1001", + "name": "fake", + "selected": true + }, + { + "date_added": "1356998400000", + "id": "1002", + "name": "test_mp3", + "selected": false + } + ] + }, + { + "ivr_type": "HOLD_ESCAPE_VM_THREE", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1005", + "name": "fake", + "selected": true + } + ] + }, + { + "ivr_type": "HOLD_MUSIC", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1006", + "name": "fake", + "selected": true + } + ] + }, + { + "ivr_type": "HOLD_POSITION_ZERO", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1004", + "name": "fake", + "selected": true + } + ] + }, + { + "ivr_type": "HOLD_WAIT", + "ivrs": [ + { + "date_added": "1356998400000", + "id": "1003", + "name": "fake", + "selected": true + } + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Custom IVR -- Get", + "tags": [ + "customivrs" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new custom IVR for a target.\n\nAdded on June 15, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "ivr.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CreateCustomIvrMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "date_added": "1356998400000", + "id": "1002", + "name": "test name", + "selected": true + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrDetailsProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Custom IVR -- Create", + "tags": [ + "customivrs" + ] + } + }, + "/api/v2/customivrs/{ivr_id}": { + "patch": { + "deprecated": false, + "description": "Update the name or description of an existing custom ivr.\n\nRate limit: 1200 per minute.", + "operationId": "ivr_details.update", + "parameters": [ + { + "description": "The ID of the custom ivr to be updated.", + "in": "path", + "name": "ivr_id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.UpdateCustomIvrDetailsMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.custom_ivr.CustomIvrDetailsProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Custom IVR -- Update", + "tags": [ + "customivrs" + ] + } + }, + "/api/v2/departments/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a department by id.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "departments.delete", + "parameters": [ + { + "description": "The department's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Departments-- Delete", + "tags": [ + "departments" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a department by id. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.get", + "parameters": [ + { + "description": "The department's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "auto_call_recording": false, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "hold_queue": { + "max_hold_count": "50", + "max_hold_seconds": "900" + }, + "hours_on": false, + "id": "130", + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "Sales", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Department -- Get", + "tags": [ + "departments" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a new department.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "departments.update", + "parameters": [ + { + "description": "The call center's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UpdateDepartmentMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Departments-- Update", + "tags": [ + "departments" + ] + } + }, + "/api/v2/departments": { + "get": { + "deprecated": false, + "description": "Gets all the departments in the company. Added on Feb 3rd, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.listall", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "filter departments by office.", + "in": "query", + "name": "office_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + }, + { + "description": "search departments by name or search by the substring of the name. If input example is 'Happy', output example can be a list of departments whose name contains the string Happy - ['Happy department 1', 'Happy department 2']", + "in": "query", + "name": "name_search", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "auto_call_recording": false, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "hold_queue": { + "max_hold_count": "50", + "max_hold_seconds": "900" + }, + "hours_on": false, + "id": "130", + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "Sales", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Department -- List", + "tags": [ + "departments" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new department.\n\nAdded on March 25th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "departments.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.CreateDepartmentMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "auto_call_recording": true, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "group_description": "Happy department", + "hold_queue": { + "allow_queuing": true, + "max_hold_count": "34", + "max_hold_seconds": "770" + }, + "hours_on": true, + "id": "1129", + "monday_hours": [ + "09:00", + "12:00", + "14:30", + "17:00" + ], + "name": "department 2046", + "no_operators_action": "voicemail", + "office_id": "124", + "ring_seconds": "45", + "routing_options": { + "closed": { + "action": "operator", + "action_target_id": "2", + "action_target_type": "user", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + }, + { + "input": "3", + "options": { + "action": "voicemail" + } + } + ], + "operator_routing": "fixedorder", + "try_dial_operators": true + }, + "open": { + "action": "menu", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + }, + { + "input": "4", + "options": { + "action": "bridge_target", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": false, + "auto_start": false + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Departments-- Create", + "tags": [ + "departments" + ] + } + }, + "/api/v2/departments/{id}/operators": { + "delete": { + "deprecated": false, + "description": "Removes an operator from a department.\n\nAdded on October 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.operators.delete", + "parameters": [ + { + "description": "The department's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.RemoveOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "1127", + "image_url": "https://dialpad.example.com/avatar/room_contact/1127.png?version=184ac3360af3ab59f685ca5f7ab4c7b1", + "is_on_duty": false, + "name": "fake-room", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Remove", + "tags": [ + "departments" + ] + }, + "get": { + "deprecated": false, + "description": "Gets operators for a department. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.operators.get", + "parameters": [ + { + "description": "The department's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "rooms": [ + { + "company_id": "123", + "country": "us", + "id": "1127", + "image_url": "https://dialpad.example.com/avatar/room_contact/1127.png?version=184ac3360af3ab59f685ca5f7ab4c7b1", + "is_free": false, + "is_on_duty": false, + "name": "fake-room", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active" + } + ], + "users": [ + { + "admin_office_ids": [ + "124" + ], + "company_id": "123", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "\u30c6\u00c9st Bot", + "do_not_disturb": false, + "emails": [ + "bot@fuzz-ball.com" + ], + "extension": "20000", + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "130", + "group_type": "department", + "role": "admin" + } + ], + "id": "2", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIRCxILVXNlclByb2ZpbGUYAgw.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- List", + "tags": [ + "departments" + ] + }, + "post": { + "deprecated": false, + "description": "Adds an operator to a department.\n\nAdded on October 2, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.operators.post", + "parameters": [ + { + "description": "The department's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.AddOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Add", + "tags": [ + "departments" + ] + } + }, + "/api/v2/faxline": { + "post": { + "deprecated": false, + "description": "Assigns a fax line to a target. Target includes user and department. Depending on the chosen line type, the number will be taken from the company's reserved pool if there are available reserved numbers, otherwise numbers can be auto-assigned using a provided area code.\n\nAdded on January 13, 2025 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "faxline.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.faxline.CreateFaxNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.faxline.FaxNumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Fax Line -- Assign", + "tags": [ + "faxline" + ] + } + }, + "/api/v2/numbers/{number}/assign": { + "post": { + "deprecated": false, + "description": "Assigns a number to a target. Target includes user, department, office, room, callcenter,\ncallrouter, staffgroup, channel and coachinggroup. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code.\n\nAdded on May 26, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_number.post", + "parameters": [ + { + "description": "A specific number to assign", + "in": "path", + "name": "number", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberTargetMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551003", + "office_id": "124", + "status": "office", + "target_id": "124", + "target_type": "office", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Assign", + "tags": [ + "numbers" + ] + } + }, + "/api/v2/numbers/assign": { + "post": { + "deprecated": false, + "description": "Assigns a number to a target. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. Target includes user, department, office, room, callcenter, callrouter,\nstaffgroup, channel and coachinggroup.\n\nAdded on November 18, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_target_number.post", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberTargetGenericMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551002", + "office_id": "124", + "status": "user", + "target_id": "2", + "target_type": "user", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Auto-Assign", + "tags": [ + "numbers" + ] + } + }, + "/api/v2/numbers/{number}": { + "delete": { + "deprecated": false, + "description": "Un-assigns a phone number from a target. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released.\n\nAdded on Jan 28, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.delete", + "parameters": [ + { + "description": "A phone number (e164 format).", + "in": "path", + "name": "number", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Releases the number (does not return it to the company reserved pool).", + "in": "query", + "name": "release", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551001", + "office_id": "124", + "status": "available", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Unassign", + "tags": [ + "numbers" + ] + }, + "get": { + "deprecated": false, + "description": "Gets number details by number.\n\nAdded on May 3, 2018 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.get", + "parameters": [ + { + "description": "A phone number (e164 format).", + "in": "path", + "name": "number", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551002", + "office_id": "124", + "status": "available", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Get", + "tags": [ + "numbers" + ] + } + }, + "/api/v2/numbers": { + "get": { + "deprecated": false, + "description": "Gets all numbers in your company.\n\nAdded on May 3, 2018 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Status to filter by.", + "in": "query", + "name": "status", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "area_code": "415", + "company_id": "123", + "number": "+14155551000", + "office_id": "124", + "status": "office", + "target_id": "124", + "target_type": "office", + "type": "local" + }, + { + "area_code": "415", + "company_id": "123", + "number": "+14155551001", + "office_id": "124", + "status": "user", + "target_id": "2", + "target_type": "user", + "type": "local" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- List", + "tags": [ + "numbers" + ] + } + }, + "/api/v2/numbers/swap": { + "post": { + "deprecated": false, + "description": "Swaps a target's primary number with a new one.\n- If a specific number is provided (`type: 'provided_number'`), the target\u2019s primary number is swapped with that number. The provided number must be available in the company\u2019s reserved pool,\nand the `reserve_pool` experiment must be enabled for the company.\n- If an area code is provided (`type: 'area_code'`), an available number from that area code is assigned.\n- If neither is provided (`type: 'auto'`), a number is automatically assigned \u2014 first from the company\u2019s reserved pool (if available), otherwise from the target\u2019s office area code. If no type is specified, 'auto' is used by default.\n\nAdded on Mar 28, 2025 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.swap_number.post", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.SwapNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Swap", + "tags": [ + "numbers" + ] + } + }, + "/api/v2/numbers/format": { + "post": { + "deprecated": false, + "description": "Used to convert local number to E.164 or E.164 to local format.\n\nAdded on June 15, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "format.post", + "parameters": [ + { + "description": "Country code in ISO 3166-1 alpha-2 format such as \"US\". Required when sending local formatted phone number", + "in": "query", + "name": "country_code", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Phone number in local or E.164 format", + "in": "query", + "name": "number", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.e164_format.FormatNumberResponse" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Phone String -- Reformat", + "tags": [ + "numbers" + ] + } + }, + "/oauth2/authorize": { + "get": { + "deprecated": false, + "description": "Initiate the OAuth flow to grant an application access to Dialpad resources on behalf of a user.", + "operationId": "oauth2.authorize.get", + "parameters": [ + { + "description": "PKCE challenge method (hashing algorithm).", + "in": "query", + "name": "code_challenge_method", + "required": false, + "schema": { + "enum": [ + "S256", + "plain" + ], + "type": "string" + } + }, + { + "description": "PKCE challenge value (hash commitment).", + "in": "query", + "name": "code_challenge", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Space-separated list of additional scopes that should be granted to the vended token.", + "in": "query", + "name": "scope", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The OAuth flow to perform. Must be 'code' (authorization code flow).", + "in": "query", + "name": "response_type", + "required": false, + "schema": { + "enum": [ + "code" + ], + "type": "string" + } + }, + { + "description": "The URI the user should be redirected back to after granting consent to the app.", + "in": "query", + "name": "redirect_uri", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The client_id of the OAuth app.", + "in": "query", + "name": "client_id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Unpredictable token to prevent CSRF.", + "in": "query", + "name": "state", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "A successful response" + } + }, + "summary": "Token -- Authorize", + "tags": [ + "oauth2" + ] + } + }, + "/oauth2/deauthorize": { + "post": { + "deprecated": false, + "description": "Revokes oauth2 tokens for a given oauth app.", + "operationId": "oauth2.deauthorize.post", + "parameters": [], + "responses": { + "204": { + "description": "A successful response" + } + }, + "summary": "Token -- Deauthorize", + "tags": [ + "oauth2" + ] + } + }, + "/oauth2/token": { + "post": { + "deprecated": false, + "description": "Exchanges a temporary oauth code for an authorized access token.", + "operationId": "oauth2.token.post", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/schemas.oauth.AuthorizationCodeGrantBodySchema" + }, + { + "$ref": "#/components/schemas/schemas.oauth.RefreshTokenGrantBodySchema" + } + ] + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.oauth.AuthorizeTokenResponseBodySchema" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Token -- Redeem", + "tags": [ + "oauth2" + ] + } + }, + "/api/v2/offices/{office_id}/plan": { + "get": { + "deprecated": false, + "description": "Gets the plan for an office.\n\nAdded on Mar 19, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "plan.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "office_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "additional_number_lines": "0", + "balance": "10.0000", + "contact_center_lines": "0", + "fax_lines": "0", + "ppu_address": { + "address_line_2": "", + "city": "SAN FRANCISCO", + "country": "us", + "postal_code": "94111", + "region": "CA" + }, + "room_lines": "0", + "sell_lines": "0", + "talk_lines": "0", + "tollfree_additional_number_lines": "0", + "tollfree_room_lines": "0", + "tollfree_uberconference_lines": "0", + "uberconference_lines": "0" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.plan.PlanProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Billing Plan -- Get", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{office_id}/callcenters": { + "get": { + "deprecated": false, + "description": "Gets all the call centers for an office. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "callcenters.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The office's id.", + "in": "path", + "name": "office_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "advanced_settings": { + "auto_call_recording": { + "call_recording_inbound": false, + "call_recording_outbound": false + }, + "max_wrap_up_seconds": "0" + }, + "alerts": { + "cc_service_level": "95", + "cc_service_level_seconds": "60" + }, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "hold_queue": { + "announce_position": true, + "announcement_interval_seconds": "120", + "max_hold_count": "50", + "max_hold_seconds": "900", + "queue_callback_dtmf": "9", + "queue_callback_threshold": "5", + "queue_escape_dtmf": "*" + }, + "hours_on": false, + "id": "1000", + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "call center 2049", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "longestidle", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.CallCenterCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Centers -- List", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{office_id}/teams": { + "get": { + "deprecated": false, + "description": "Get a list of coaching teams of a office. Added on Jul 30th, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "coaching_team.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The office's id.", + "in": "path", + "name": "office_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "allow_trainee_eavesdrop": true, + "company_id": "123", + "country": "us", + "id": "1001", + "name": "team_name", + "office_id": "124", + "state": "active" + }, + { + "allow_trainee_eavesdrop": true, + "company_id": "123", + "country": "us", + "id": "1130", + "name": "team_name2", + "office_id": "124", + "state": "active" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.coaching_team.CoachingTeamCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Coaching Team -- List", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{office_id}/departments": { + "get": { + "deprecated": false, + "description": "Gets all the departments for an office. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "departments.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The office's id.", + "in": "path", + "name": "office_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "auto_call_recording": false, + "availability_status": "open", + "country": "us", + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "hold_queue": { + "max_hold_count": "50", + "max_hold_seconds": "900" + }, + "hours_on": false, + "id": "130", + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "Sales", + "no_operators_action": "voicemail", + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "voice_intelligence": { + "allow_pause": true, + "auto_start": true + }, + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.DepartmentCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Department -- List", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}/assign_number": { + "post": { + "deprecated": false, + "description": "Assigns a number to a office. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code.\n\nAdded on March 19, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_office_number.post", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "604", + "company_id": "123", + "number": "+16045551002", + "office_id": "124", + "status": "office", + "target_id": "124", + "target_type": "office", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Assign", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}/unassign_number": { + "post": { + "deprecated": false, + "description": "Un-assigns a phone number from a office mainline. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released.\n\nAdded on March 19, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.office_unassign_number.post", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.UnassignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "deleted": true + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Unassign", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}/e911": { + "get": { + "deprecated": false, + "description": "Gets E911 address of the office by office id.\n\nAdded on May 25, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.e911.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "address": "3001 BISHOP DR", + "address2": "ste 120", + "city": "SAN RAMON", + "country": "us", + "state": "ca", + "zip": "94583" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.E911GetProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "E911 Address -- Get", + "tags": [ + "offices" + ] + }, + "put": { + "deprecated": false, + "description": "Update E911 address of the given office.\n\nAdded on May 25, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.e911.update", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.office.E911UpdateMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "address": "3001 BISHOP DR", + "address2": "ste 120", + "city": "SAN RAMON", + "country": "us", + "state": "ca", + "zip": "94583" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.E911GetProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "E911 Address -- Update", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{office_id}/available_licenses": { + "get": { + "deprecated": false, + "description": "Gets the available licenses for an office.\n\nAdded on July 2, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "plan.available_licenses.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "office_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "additional_number_lines": "0", + "contact_center_lines": "0", + "fax_lines": "1", + "room_lines": "5", + "sell_lines": "0", + "talk_lines": "4", + "tollfree_additional_number_lines": "0", + "tollfree_room_lines": "0", + "uberconference_lines": "0" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.plan.AvailableLicensesProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Licenses -- List Available", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}/offdutystatuses": { + "get": { + "deprecated": false, + "description": "Lists Off-Duty status values.\n\nRate limit: 1200 per minute.", + "operationId": "offices.offdutystatuses.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "id": "124", + "off_duty_statuses": [ + "Lunch Time", + "Off Duty" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.OffDutyStatusesProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Off-Duty Status -- List", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}": { + "get": { + "deprecated": false, + "description": "Gets an office by id.\n\nAdded on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "availability_status": "open", + "country": "us", + "e911_address": { + "address": "address", + "city": "city", + "country": "us", + "state": "state", + "zip": "94111" + }, + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "id": "124", + "is_primary_office": true, + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "\u30d5\u30a1\u30fc\u30b8\u30fc\u3000\u30dc\u30fc\u30eb, Inc", + "no_operators_action": "voicemail", + "office_id": "124", + "office_settings": { + "allow_device_guest_login": false, + "block_caller_id_disabled": false, + "bridged_target_recording_allowed": true, + "disable_desk_phone_self_provision": false, + "disable_ivr_voicemail": false, + "no_recording_message_on_user_calls": false, + "set_caller_id_disabled": false + }, + "phone_numbers": [ + "+14155551000" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.OfficeProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Office -- Get", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices": { + "get": { + "deprecated": false, + "description": "Gets all the offices that are accessible using your api key.\n\nAdded on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Whether we only return active offices.", + "in": "query", + "name": "active_only", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "availability_status": "open", + "country": "us", + "e911_address": { + "address": "address", + "city": "city", + "country": "us", + "state": "state", + "zip": "94111" + }, + "first_action": "operators", + "friday_hours": [ + "08:00", + "18:00" + ], + "id": "124", + "is_primary_office": true, + "monday_hours": [ + "08:00", + "18:00" + ], + "name": "\u30d5\u30a1\u30fc\u30b8\u30fc\u3000\u30dc\u30fc\u30eb, Inc", + "no_operators_action": "voicemail", + "office_id": "124", + "office_settings": { + "allow_device_guest_login": false, + "block_caller_id_disabled": false, + "bridged_target_recording_allowed": true, + "disable_desk_phone_self_provision": false, + "disable_ivr_voicemail": false, + "no_recording_message_on_user_calls": false, + "set_caller_id_disabled": false + }, + "phone_numbers": [ + "+14155551000" + ], + "ring_seconds": "30", + "routing_options": { + "closed": { + "action": "voicemail", + "dtmf": [ + { + "input": "0", + "options": { + "action": "disabled" + } + }, + { + "input": "1", + "options": { + "action": "directory" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": false + }, + "open": { + "action": "department", + "action_target_id": "130", + "action_target_type": "department", + "dtmf": [ + { + "input": "0", + "options": { + "action": "operator" + } + }, + { + "input": "1", + "options": { + "action": "department", + "action_target_id": "124", + "action_target_type": "office" + } + } + ], + "operator_routing": "simultaneous", + "try_dial_operators": true + } + }, + "state": "active", + "thursday_hours": [ + "08:00", + "18:00" + ], + "timezone": "US/Pacific", + "tuesday_hours": [ + "08:00", + "18:00" + ], + "wednesday_hours": [ + "08:00", + "18:00" + ] + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.OfficeCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Office -- List", + "tags": [ + "offices" + ] + }, + "post": { + "deprecated": false, + "description": "\n\nRate limit: 1200 per minute.", + "operationId": "offices.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.office.CreateOfficeMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.office.OfficeUpdateResponse" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Office -- POST Creates a secondary office.", + "tags": [ + "offices" + ] + } + }, + "/api/v2/offices/{id}/operators": { + "delete": { + "deprecated": false, + "description": "Removes an operator from office's mainline.\n\nAdded on Sep 22, 2023 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.operators.delete", + "parameters": [ + { + "description": "The office's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.RemoveOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Remove", + "tags": [ + "offices" + ] + }, + "get": { + "deprecated": false, + "description": "Gets mainline operators for an office. Added on May 1, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.operators.get", + "parameters": [ + { + "description": "The office's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "users": [ + { + "admin_office_ids": [ + "124" + ], + "company_id": "123", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "\u30c6\u00c9st Bot", + "do_not_disturb": false, + "emails": [ + "bot@fuzz-ball.com" + ], + "extension": "20000", + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "124", + "group_type": "office", + "role": "admin" + } + ], + "id": "2", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIRCxILVXNlclByb2ZpbGUYAgw.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "international_dialing_enabled": false, + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "124", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.group.OperatorCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- List", + "tags": [ + "offices" + ] + }, + "post": { + "deprecated": false, + "description": "Adds an operator into office's mainline.\n\nAdded on Sep 22, 2023 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "offices.operators.post", + "parameters": [ + { + "description": "The office's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.AddOperatorMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.group.UserOrRoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Operator -- Add", + "tags": [ + "offices" + ] + } + }, + "/api/v2/recordingsharelink": { + "post": { + "deprecated": false, + "description": "Creates a recording share link.\n\nAdded on Aug 26, 2021 for API v2.\n\nRate limit: 100 per minute.", + "operationId": "recording_share_link.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.CreateRecordingShareLink", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "access_link": "https://dialpad.example.com/recording/voicemail/l335pbEHG75xyw9zLaedu1ujLxPr5xFpa4g5HpdOgvbE", + "call_id": "1006", + "created_by_id": "2", + "date_added": "2013-01-01T00:00:00", + "id": "l335pbEHG75xyw9zLaedu1ujLxPr5xFpa4g5HpdOgvbE", + "item_id": "uuid-+14155558888-1", + "privacy": "owner", + "type": "voicemail" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.RecordingShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Recording Sharelink -- Create", + "tags": [ + "recordingsharelink" + ] + } + }, + "/api/v2/recordingsharelink/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a recording share link by id.\n\nAdded on Aug 26, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "recording_share_link.delete", + "parameters": [ + { + "description": "The recording share link's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "access_link": "https://dialpad.example.com/recording/voicemail/VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "call_id": "1000", + "created_by_id": "2", + "date_added": "2013-01-01T00:00:00", + "id": "VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "item_id": "uuid-+14155557777-1", + "privacy": "public", + "type": "voicemail" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.RecordingShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Recording Sharelink -- Delete", + "tags": [ + "recordingsharelink" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a recording share link by id.\n\nAdded on Aug 26, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "recording_share_link.get", + "parameters": [ + { + "description": "The recording share link's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "access_link": "https://dialpad.example.com/recording/voicemail/VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "call_id": "1000", + "created_by_id": "2", + "date_added": "2013-01-01T00:00:00", + "id": "VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "item_id": "uuid-+14155557777-1", + "privacy": "owner", + "type": "voicemail" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.RecordingShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Recording Sharelink -- Get", + "tags": [ + "recordingsharelink" + ] + }, + "put": { + "deprecated": false, + "description": "Updates a recording share link by id.\n\nAdded on Aug 26, 2021 for API v2.\n\nRate limit: 100 per minute.", + "operationId": "recording_share_link.update", + "parameters": [ + { + "description": "The recording share link's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.UpdateRecordingShareLink", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "access_link": "https://dialpad.example.com/recording/voicemail/VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "call_id": "1000", + "created_by_id": "2", + "date_added": "2013-01-01T00:00:00", + "id": "VwJDwUByepqAEtBGgU6TNVqcI7pktNtWmkJRp8ysGQrm", + "item_id": "uuid-+14155557777-1", + "privacy": "company", + "type": "voicemail" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.recording_share_link.RecordingShareLink" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Recording Sharelink -- Update", + "tags": [ + "recordingsharelink" + ] + } + }, + "/api/v2/rooms/{id}/assign_number": { + "post": { + "deprecated": false, + "description": "Assigns a number to a room. The number will automatically be taken from the company's reserved block if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code.\n\nAdded on March 19, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_room_number.post", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "604", + "company_id": "123", + "number": "+16045551003", + "office_id": "124", + "status": "room", + "target_id": "1000", + "target_type": "room", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Assign", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms/{id}/unassign_number": { + "post": { + "deprecated": false, + "description": "Un-assigns a phone number from a room. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released.\n\nAdded on March 19, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.room_unassign_number.post", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.UnassignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "deleted": true + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Unassign", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a room by id.\n\nAdded on Mar 8, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "rooms.delete", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "1000", + "image_url": "https://dialpad.example.com/avatar/room_contact/1000.png?version=e7bd09045d55362221e538a25a7f1891", + "is_free": false, + "is_on_duty": false, + "name": "Blackcomb", + "office_id": "124", + "state": "pending" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.RoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room -- Delete", + "tags": [ + "rooms" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a room by id.\n\nAdded on Aug 13, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "rooms.get", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "1001", + "image_url": "https://dialpad.example.com/avatar/room_contact/1001.png?version=1ab1c29014c30c2a5d6258f673dbdf19", + "is_free": false, + "is_on_duty": false, + "name": "Boulevard", + "office_id": "124", + "phone_numbers": [ + "+14155551003" + ], + "state": "active" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.RoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room -- Get", + "tags": [ + "rooms" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates room details by id.\n\nAdded on Mar 8, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "rooms.patch", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.room.UpdateRoomMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "1000", + "image_url": "https://dialpad.example.com/avatar/room_contact/1000.png?version=e7bd09045d55362221e538a25a7f1891", + "is_free": false, + "is_on_duty": false, + "name": "Blackcomb", + "office_id": "124", + "state": "pending" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.RoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room -- Update", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms": { + "get": { + "deprecated": false, + "description": "Gets all rooms in your company, optionally filtering by office.\n\nAdded on Aug 13, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "rooms.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The office's id.", + "in": "query", + "name": "office_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "company_id": "123", + "country": "us", + "id": "1000", + "image_url": "https://dialpad.example.com/avatar/room_contact/1000.png?version=5494b35020bb8a92d06d338b3b2cfd80", + "is_free": false, + "is_on_duty": false, + "name": "Seaside", + "office_id": "124", + "phone_numbers": [ + "+14155551002" + ], + "state": "active" + }, + { + "company_id": "123", + "country": "us", + "id": "1001", + "image_url": "https://dialpad.example.com/avatar/room_contact/1001.png?version=1ab1c29014c30c2a5d6258f673dbdf19", + "is_free": false, + "is_on_duty": false, + "name": "Boulevard", + "office_id": "124", + "phone_numbers": [ + "+14155551003" + ], + "state": "active" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.RoomCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room -- List", + "tags": [ + "rooms" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new room.\n\nAdded on Mar 8, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "rooms.post", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.room.CreateRoomMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "123", + "country": "us", + "id": "1000", + "image_url": "https://dialpad.example.com/avatar/room_contact/1000.png?version=74c29ceb0dbe5ebf5c2cd0742e283727", + "is_free": false, + "is_on_duty": false, + "name": "Whistler", + "office_id": "124", + "state": "pending" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.RoomProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room -- Create", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms/international_pin": { + "post": { + "deprecated": false, + "description": "Assigns a PIN for making international calls from rooms\n\nWhen PIN protected international calls are enabled for the company, a PIN is required to make international calls from room phones.\n\nAdded on Aug 16, 2018 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.rooms.create_international_pin", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.room.CreateInternationalPinProto", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "customer_ref": "we_work_user", + "expires_on": "2013-01-01T00:30:00", + "pin": "84442" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.room.InternationalPinProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room Phone -- Assign PIN", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms/{parent_id}/deskphones/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a room desk phone by id. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.rooms.delete", + "parameters": [ + { + "description": "The desk phone's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The room's id.", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Room Phone -- Delete", + "tags": [ + "rooms" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a room desk phone by id. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.rooms.get", + "parameters": [ + { + "description": "The desk phone's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The room's id.", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "byod": false, + "id": "+14155551002-room-1000-obi-hex_ce3e13a487", + "mac_address": "9cadefa00096", + "name": "Test Obihai", + "owner_id": "1000", + "owner_type": "room", + "port": "7060", + "realm": "uvstaging.ubervoip.net", + "ring_notification": true, + "type": "obi" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.deskphone.DeskPhone" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room Phone -- Get", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/rooms/{parent_id}/deskphones": { + "get": { + "deprecated": false, + "description": "Gets all desk phones under a room. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.rooms.list", + "parameters": [ + { + "description": "The room's id.", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "byod": false, + "id": "+14155551002-room-1000-obi-hex_ce3e13a487", + "mac_address": "9cadefa00096", + "name": "Test Obihai", + "owner_id": "1000", + "owner_type": "room", + "port": "7060", + "realm": "uvstaging.ubervoip.net", + "ring_notification": true, + "type": "obi" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.deskphone.DeskPhoneCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Room Phone -- List", + "tags": [ + "rooms" + ] + } + }, + "/api/v2/schedulereports/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports.\n\nAdded on Jul 6, 2022 for API v2\n\nRate limit: 1200 per minute.", + "operationId": "schedule_reports.delete", + "parameters": [ + { + "description": "The schedule reports subscription's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Schedule reports -- Delete", + "tags": [ + "schedulereports" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports.\n\nAdded on Jul 6, 2022 for API v2\n\nRate limit: 1200 per minute.", + "operationId": "schedule_reports.get", + "parameters": [ + { + "description": "The schedule reports subscription's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Schedule reports -- Get", + "tags": [ + "schedulereports" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports.\n\nAdded on Jul 6, 2022 for API v2\n\nRate limit: 1200 per minute.", + "operationId": "schedule_reports.update", + "parameters": [ + { + "description": "The schedule reports subscription's ID.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ProcessScheduleReportsMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Schedule reports -- Update", + "tags": [ + "schedulereports" + ] + } + }, + "/api/v2/schedulereports": { + "get": { + "deprecated": false, + "description": "Lists all schedule reports subscription for a company. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports.\n\nAdded on Jul 6, 2022 for API v2\n\nRate limit: 1200 per minute.", + "operationId": "schedule_reports.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Schedule reports -- List", + "tags": [ + "schedulereports" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a schedule reports subscription for your company. An endpoint_id is required in order to receive the event payload and can be obtained via websockets or webhooks. A schedule reports is a mechanism to schedule daily, weekly or monthly record and statistics reports.\n\nAdded on Jun 17, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "schedule_reports.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ProcessScheduleReportsMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.schedule_reports.ScheduleReportsStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "schedule reports -- Create", + "tags": [ + "schedulereports" + ] + } + }, + "/api/v2/sms": { + "post": { + "deprecated": false, + "description": "Sends an SMS message to a phone number or to a Dialpad channel on behalf of a user.\n\nAdded on Dec 18, 2019 for API v2.\n\nTier 0 Rate limit: 100 per minute.\n\nTier 1 Rate limit: 800 per minute.\n\n", + "operationId": "sms.send", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.sms.SendSMSMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "contact_id": "1000", + "created_date": "2013-01-01T00:00:00", + "device_type": "public_api", + "direction": "outbound", + "from_number": "+14155551001", + "id": "1004", + "message_status": "pending", + "target_id": "2", + "target_type": "user", + "text": "Test text", + "to_numbers": [ + "+14155557777" + ], + "user_id": "2" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms.SMSProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS -- Send", + "tags": [ + "sms" + ] + } + }, + "/api/v2/stats/{id}": { + "get": { + "deprecated": false, + "description": "Gets the progress and result of a statistics request.\n\nAdded on May 3, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "stats.get", + "parameters": [ + { + "description": "Request ID returned by a POST /stats request.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "status": "processing" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.stats.StatsProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Stats -- Get Result", + "tags": [ + "stats" + ] + } + }, + "/api/v2/stats": { + "post": { + "deprecated": false, + "description": "Begins processing statistics asynchronously, returning a request id to get the status and retrieve the results by calling GET /stats/{request_id}.\n\nStats for the whole company will be processed by default. An office_id can be provided to limit stats to a single office. A target_id and target_type can be provided to limit stats to a single target.\n\nAdded on May 3, 2018 for API v2.\n\nRate limit: 200 per hour.", + "operationId": "stats.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.stats.ProcessStatsMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.stats.ProcessingProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Stats -- Initiate Processing", + "tags": [ + "stats" + ] + } + }, + "/api/v2/subscriptions/agent_status": { + "get": { + "deprecated": false, + "description": "Gets a list of all the agent status event subscriptions of a company.\n\nAdded on May 7th, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_agent_status_event_subscription.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "agent_type": "callcenter", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Agent Status -- List", + "tags": [ + "subscriptions" + ] + }, + "post": { + "deprecated": false, + "description": "Creates an agent status event subscription for your company. A webhook_id is required so that we know to which url the events shall be sent. Please be aware that only call center agent is supported for agent event subscription now.\n\nSee https://developers.dialpad.com/docs/agent-status-events for details on how agent status events work, including the payload structure and payload examples.\n\nAdded on May 7th, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_agent_status_event_subscription.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.CreateAgentStatusEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "agent_type": "callcenter", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Agent Status -- Create", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/agent_status/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes an agent status event subscription by id.\n\nAdded on May 7th, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_agent_status_event_subscription.delete", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "agent_type": "callcenter", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Agent Status -- Delete", + "tags": [ + "subscriptions" + ] + }, + "get": { + "deprecated": false, + "description": "Gets an agent status event subscription by id.\n\nAdded on May 7th, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_agent_status_event_subscription.get", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "agent_type": "callcenter", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Agent Status -- Get", + "tags": [ + "subscriptions" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates an agent status event subscription by id.\n\nAdded on May 7th, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_agent_status_event_subscription.update", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.UpdateAgentStatusEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "agent_type": "callcenter", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.agent_status_event_subscription.AgentStatusEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Agent Status -- Update", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/call": { + "get": { + "deprecated": false, + "description": "Gets a list of all the call event subscriptions of a company or of a target.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_call_event_subscription.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Target's type.", + "in": "query", + "name": "target_type", + "required": false, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + }, + { + "description": "The target's id.", + "in": "query", + "name": "target_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "call_states": [ + "barge", + "hangup", + "recording" + ], + "enabled": true, + "group_calls_only": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Event -- List", + "tags": [ + "subscriptions" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a call event subscription. A webhook_id is required so that we know to which url the events shall be sent. Call states can be used to limit the states for which call events are sent. A target_type and target_id may optionally be provided to scope the events only to the calls to/from that target.\n\nSee https://developers.dialpad.com/docs/call-events-logging for details on how call events work,\nincluding the payload structure, the meaning of different call states, and payload examples.\n\nNote: **To include the recording url in call events, your API key needs to have the\n\"recordings_export\" OAuth scope. For Dialpad Meetings call events, your API key needs to have the \"conference:all\" OAuth scope.**\n\nAdded on April 23rd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_call_event_subscription.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CreateCallEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_states": [ + "barge", + "hangup", + "recording" + ], + "enabled": true, + "group_calls_only": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Event -- Create", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/call/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a call event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_call_event_subscription.delete", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_states": [ + "calling" + ], + "enabled": true, + "group_calls_only": false, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Event -- Delete", + "tags": [ + "subscriptions" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a call event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_call_event_subscription.get", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_states": [ + "barge", + "hangup", + "recording" + ], + "enabled": true, + "group_calls_only": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Event -- Get", + "tags": [ + "subscriptions" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a call event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_call_event_subscription.update", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.UpdateCallEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_states": [ + "calling" + ], + "enabled": true, + "group_calls_only": false, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call_event_subscription.CallEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Event -- Update", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/changelog": { + "get": { + "deprecated": false, + "description": "Gets a list of all the change log event subscriptions of a company.\n\nAdded on December 9th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``change_log``\n\nRate limit: 1200 per minute.", + "operationId": "webhook_change_log_event_subscription.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionCollection" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "change_log" + ] + }, + { + "bearer_token": [ + "change_log" + ] + } + ], + "summary": "Change Log -- List", + "tags": [ + "subscriptions" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a change log event subscription for your company. An endpoint_id is required so that we know to which url the events shall be sent.\n\nSee https://developers.dialpad.com/docs/change-log-events for details on how change log events work, including the payload structure and payload examples.\n\nAdded on December 9th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``change_log``\n\nRate limit: 1200 per minute.", + "operationId": "webhook_change_log_event_subscription.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.CreateChangeLogEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "change_log" + ] + }, + { + "bearer_token": [ + "change_log" + ] + } + ], + "summary": "Change Log -- Create", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/changelog/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a change log event subscription by id.\n\nAdded on December 9th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``change_log``\n\nRate limit: 1200 per minute.", + "operationId": "webhook_change_log_event_subscription.delete", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "change_log" + ] + }, + { + "bearer_token": [ + "change_log" + ] + } + ], + "summary": "Change Log -- Delete", + "tags": [ + "subscriptions" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a change log event subscription by id.\n\nAdded on December 9th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``change_log``\n\nRate limit: 1200 per minute.", + "operationId": "webhook_change_log_event_subscription.get", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "change_log" + ] + }, + { + "bearer_token": [ + "change_log" + ] + } + ], + "summary": "Change Log -- Get", + "tags": [ + "subscriptions" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates change log event subscription by id.\n\nAdded on December 9th, 2022 for API v2.\n\nRequires a company admin API key.\n\nRequires scope: ``change_log``\n\nRate limit: 1200 per minute.", + "operationId": "webhook_change_log_event_subscription.update", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.UpdateChangeLogEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.change_log_event_subscription.ChangeLogEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "change_log" + ] + }, + { + "bearer_token": [ + "change_log" + ] + } + ], + "summary": "Change Log -- Update", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/contact": { + "get": { + "deprecated": false, + "description": "Gets a list of all the contact event subscriptions of a company.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_contact_event_subscription.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "contact_type": "local", + "enabled": true, + "id": "1001", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + }, + { + "contact_type": "shared", + "enabled": true, + "id": "1002", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact Event -- List", + "tags": [ + "subscriptions" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a contact event subscription for your company. A webhook_id is required so that we know to which url the events shall be sent.\n\nSee https://developers.dialpad.com/docs/contact-events for details on how contact events work, including the payload structure and payload examples.\n\nAdded on April 23rd, 2021 for API v2.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_contact_event_subscription.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.CreateContactEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "contact_type": "shared", + "enabled": true, + "id": "1002", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact Event -- Create", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/contact/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a contact event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_contact_event_subscription.delete", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "contact_type": "local", + "enabled": true, + "id": "1002", + "webhook": { + "hook_url": "https://test2.com", + "id": "1003", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact Event -- Delete", + "tags": [ + "subscriptions" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a contact event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_contact_event_subscription.get", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "contact_type": "shared", + "enabled": true, + "id": "1002", + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact Event -- Get", + "tags": [ + "subscriptions" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a contact event subscription by id.\n\nAdded on April 23rd, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRequires a company admin API key.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_contact_event_subscription.update", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.UpdateContactEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "contact_type": "local", + "enabled": true, + "id": "1002", + "webhook": { + "hook_url": "https://test2.com", + "id": "1003", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.contact_event_subscription.ContactEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Contact Event -- Update", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/sms": { + "get": { + "deprecated": false, + "description": "Gets a list of all the SMS event subscriptions of a company or of a target.\n\nAdded on April 9th, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_sms_event_subscription.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Target's type.", + "in": "query", + "name": "target_type", + "required": false, + "schema": { + "enum": [ + "callcenter", + "callrouter", + "channel", + "coachinggroup", + "coachingteam", + "department", + "office", + "room", + "staffgroup", + "unknown", + "user" + ], + "type": "string" + } + }, + { + "description": "The target's id.", + "in": "query", + "name": "target_id", + "required": false, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "direction": "all", + "enabled": true, + "id": "1001", + "include_internal": true, + "status": false, + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS Event -- List", + "tags": [ + "subscriptions" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a SMS event subscription. A webhook_id is required so that we know to which url the events shall be sent. A SMS direction is also required in order to limit the direction for which SMS events are sent. Use 'all' to get SMS events for all directions. A target_type and target_id may optionally be provided to scope the events only to SMS to/from that target.\n\nSee https://developers.dialpad.com/docs/sms-events for details on how SMS events work, including the payload structure and payload examples.\n\nNOTE: **To include the MESSAGE CONTENT in SMS events, your API key needs to have the\n\"message_content_export\" OAuth scope for when a target is specified in this API and/or\n\"message_content_export:all\" OAuth scope for when no target is specified.**\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nAdded on April 9th, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_sms_event_subscription.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.CreateSmsEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "direction": "all", + "enabled": true, + "id": "1001", + "include_internal": true, + "status": false, + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS Event -- Create", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/subscriptions/sms/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a SMS event subscription by id.\n\nAdded on April 9th, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_sms_event_subscription.delete", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "direction": "inbound", + "enabled": true, + "id": "1001", + "include_internal": false, + "status": true, + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "random", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS Event -- Delete", + "tags": [ + "subscriptions" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a SMS event subscription by id.\n\nAdded on April 9th, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_sms_event_subscription.get", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "direction": "all", + "enabled": true, + "id": "1001", + "include_internal": true, + "status": false, + "webhook": { + "hook_url": "https://test.com", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "puppy", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS Event -- Get", + "tags": [ + "subscriptions" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a SMS event subscription by id.\n\nAdded on April 9th, 2021 for API v2.\n\nNOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs.\n\nRate limit: 1200 per minute.", + "operationId": "webhook_sms_event_subscription.update", + "parameters": [ + { + "description": "The event subscription's ID, which is generated after creating an event subscription successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.UpdateSmsEventSubscription", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "direction": "inbound", + "enabled": true, + "id": "1001", + "include_internal": false, + "status": false, + "webhook": { + "hook_url": "https://test2.com", + "id": "1002", + "signature": { + "algo": "HS256", + "secret": "random", + "type": "jwt" + } + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.sms_event_subscription.SmsEventSubscriptionProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "SMS Event -- Update", + "tags": [ + "subscriptions" + ] + } + }, + "/api/v2/transcripts/{call_id}": { + "get": { + "deprecated": false, + "description": "Gets the Dialpad AI transcript of a call, including moments.\n\nAdded on Dec 18, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "transcripts.get", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "call_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_id": "1001", + "lines": [ + { + "contact_id": "ag5kZXZ-dWJlci12b2ljZXIOCxIHQ29udGFjdBjoBww", + "content": "hi", + "name": "(415) 555-6666", + "time": "2018-05-08T21:33:19.300000", + "type": "transcript" + }, + { + "content": "hello [expletive]", + "name": "\u30c6\u00c9st Bot", + "time": "2018-05-08T21:33:15.300000", + "type": "transcript", + "user_id": "2" + }, + { + "content": "not_white_listed_moment", + "name": "\u30c6\u00c9st Bot", + "time": "2018-05-08T21:33:16.300000", + "type": "moment", + "user_id": "2" + }, + { + "content": "price_inquiry", + "name": "\u30c6\u00c9st Bot", + "time": "2018-05-08T21:33:17.300000", + "type": "moment", + "user_id": "2" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.transcript.TranscriptProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Transcript -- Get", + "tags": [ + "transcripts" + ] + } + }, + "/api/v2/transcripts/{call_id}/url": { + "get": { + "deprecated": false, + "description": "Gets the transcript url of a call.\n\nAdded on June 9, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "transcripts.get_url", + "parameters": [ + { + "description": "The call's id.", + "in": "path", + "name": "call_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_id": "1001", + "url": "https://dialpad.com/callhistory/callreview/1001?tab=transcript" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.transcript.TranscriptUrlProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Transcript -- Get URL", + "tags": [ + "transcripts" + ] + } + }, + "/api/v2/userdevices/{id}": { + "get": { + "deprecated": false, + "description": "Gets a device by ID.\n\nAdded on Feb 4, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "userdevices.get", + "parameters": [ + { + "description": "The device's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.userdevice.UserDeviceProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User Device -- Get", + "tags": [ + "userdevices" + ] + } + }, + "/api/v2/userdevices": { + "get": { + "deprecated": false, + "description": "Lists the devices for a specific user.\n\nAdded on Feb 4, 2020 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "userdevices.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "query", + "name": "user_id", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.userdevice.UserDeviceCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User Device -- List", + "tags": [ + "userdevices" + ] + } + }, + "/api/v2/users/{id}/initiate_call": { + "post": { + "deprecated": false, + "description": "Causes a user's native Dialpad application to initiate an outbound call. Added on Nov 18, 2019 for API v2.\n\nRate limit: 5 per minute.", + "operationId": "users.initiate_call", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.InitiateCallMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "device": { + "date_created": "2013-01-01T00:00:00", + "date_registered": "2013-01-01T00:00:00", + "date_updated": "2013-01-01T00:00:00", + "display_name": "native", + "id": "+14155551001-user-2-client-native", + "type": "native", + "user_id": "2" + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.InitiatedCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call -- Initiate", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/activecall": { + "patch": { + "deprecated": false, + "description": "Turns call recording on or off for a user's active call.\n\nAdded on Nov 18, 2019 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.update_active_call", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.UpdateActiveCallMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "call_state": "connected", + "id": "1000", + "is_recording": true + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.ActiveCallProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call Recording -- Toggle", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/togglevi": { + "patch": { + "deprecated": false, + "description": "Turns call vi on or off for a user's active call. Added on May 4, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.toggle_call_vi", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.call.ToggleViMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "error": { + "code": 400, + "errors": [ + { + "domain": "global", + "message": "call 1001 does not have callai", + "reason": "badRequest" + } + ], + "message": "call 1001 does not have callai" + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.call.ToggleViProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Call VI -- Toggle", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/caller_id": { + "get": { + "deprecated": false, + "description": "List all available Caller IDs and the active Called ID for a determined User id\n\nAdded on Aug 3, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "caller_id.users.get", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.caller_id.CallerIdProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Caller ID -- Get", + "tags": [ + "users" + ] + }, + "post": { + "deprecated": false, + "description": "Set Caller ID for a determined User id.\n\nAdded on Aug 3, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "caller_id.users.post", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.caller_id.SetCallerIdMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.caller_id.CallerIdProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Caller ID -- POST", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{parent_id}/deskphones/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a user desk phone by id. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.users.delete", + "parameters": [ + { + "description": "The desk phone's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "A successful response" + } + }, + "summary": "Desk Phone -- Delete", + "tags": [ + "users" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a user desk phone by id. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.users.get", + "parameters": [ + { + "description": "The desk phone's id.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "byod": false, + "id": "+14155551001-user-2-obi-hex_71afb3cbf6", + "mac_address": "9cadefa00096", + "name": "Test Obihai", + "owner_id": "2", + "owner_type": "user", + "port": "7060", + "realm": "uvstaging.ubervoip.net", + "ring_notification": true, + "sip_transport_type": "tls", + "type": "obi" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.deskphone.DeskPhone" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Desk Phone -- Get", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{parent_id}/deskphones": { + "get": { + "deprecated": false, + "description": "Gets all desk phones under a user. Added on May 17, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "deskphones.users.list", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "parent_id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "byod": false, + "id": "+14155551001-user-2-obi-hex_71afb3cbf6", + "mac_address": "9cadefa00096", + "name": "Test Obihai", + "owner_id": "2", + "owner_type": "user", + "port": "7060", + "realm": "uvstaging.ubervoip.net", + "ring_notification": true, + "sip_transport_type": "tls", + "type": "obi" + }, + { + "byod": false, + "id": "+14155551001-user-2-obi-hex_bc7abbd28f", + "mac_address": "9cadefa00097", + "name": "Test Obihai2", + "owner_id": "2", + "owner_type": "user", + "port": "7060", + "realm": "uvstaging.ubervoip.net", + "ring_notification": true, + "sip_transport_type": "tls", + "type": "obi" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.deskphone.DeskPhoneCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Desk Phone -- List", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/assign_number": { + "post": { + "deprecated": false, + "description": "Assigns a number to a user. The number will automatically be taken from the company's reserved block if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code.\n\nAdded on May 3, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.assign_user_number.post", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.AssignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551002", + "office_id": "124", + "status": "user", + "target_id": "2", + "target_type": "user", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Assign", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/unassign_number": { + "post": { + "deprecated": false, + "description": "Un-assigns a phone number from a user. The number will be returned to the company's reserved block if there is one. Otherwise the number will be released.\n\nAdded on May 3, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "numbers.user_unassign_number.post", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.number.UnassignNumberMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "area_code": "415", + "company_id": "123", + "number": "+14155551002", + "office_id": "124", + "status": "available", + "type": "local" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.number.NumberProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Dialpad Number -- Unassign", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/togglednd": { + "patch": { + "deprecated": false, + "description": "Toggle DND status on or off for the given user.\n\nAdded on Oct 14, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.toggle_dnd", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.ToggleDNDMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "do_not_disturb": true, + "group_id": "5220648944236254", + "group_type": "department", + "id": "5835806903417907" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.ToggleDNDProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Do Not Disturb -- Toggle", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/e911": { + "get": { + "deprecated": false, + "description": "Gets E911 address of the user by user id.\n\nAdded on May 25, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.e911.get", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "address": "3001 BISHOP DR", + "address2": "ste 120", + "city": "SAN RAMON", + "country": "us", + "state": "ca", + "zip": "94583" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.E911GetProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "E911 Address -- Get", + "tags": [ + "users" + ] + }, + "put": { + "deprecated": false, + "description": "Update E911 address of the given user.\n\nAdded on May 25, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.e911.update", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.E911UpdateMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "address": "3001 BISHOP DR", + "address2": "ste 120", + "city": "SAN RAMON", + "country": "us", + "state": "ca", + "zip": "94583" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.office.E911GetProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "E911 Address -- Update", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/personas": { + "get": { + "deprecated": false, + "description": "Provides a list of personas for a user.\n\nA persona is a target that a user can make calls from. The receiver of the call will see the details of the persona rather than the user.\n\nAdded on February 12, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.personas.get", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.PersonaCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Persona -- List", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/screenpop": { + "post": { + "deprecated": false, + "description": "Initiates screen pop for user device. Requires the \"screen_pop\" scope.\n\nRequires scope: ``screen_pop``\n\nRate limit: 5 per minute.", + "operationId": "screen_pop.initiate", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.screen_pop.InitiateScreenPopMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.screen_pop.InitiateScreenPopResponse" + } + } + }, + "description": "A successful response" + } + }, + "security": [ + { + "api_key_in_url": [ + "screen_pop" + ] + }, + { + "bearer_token": [ + "screen_pop" + ] + } + ], + "summary": "Screen-pop -- Trigger", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a user by id.\n\nAdded on May 11, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.delete", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "4508591728425653", + "country": "us", + "date_added": "2013-01-01T00:00:00", + "display_name": "Blocky Mockson (deactivated)", + "do_not_disturb": true, + "first_name": "Blocky", + "id": "5643742714968973", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUYjffq_cOegwoM.png?version=a54c75379069406d7958461d98788fb4", + "international_dialing_enabled": false, + "is_admin": false, + "is_available": false, + "is_on_duty": false, + "is_online": false, + "is_super_admin": false, + "language": "en", + "last_name": "Mockson", + "license": "agents", + "muted": false, + "office_id": "5220648944236254", + "state": "deleted", + "status_message": "", + "timezone": "US/Pacific" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.UserProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- Delete", + "tags": [ + "users" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a user by id.\n\nAdded on March 22, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.get", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "admin_office_ids": [ + "6422934115149452" + ], + "company_id": "4508591728425653", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "\u30c6\u00c9st Bot", + "do_not_disturb": false, + "duty_status_started": "2013-01-01T00:00:00", + "emails": [ + "bot@fuzz-ball.com" + ], + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "5220648944236254", + "group_type": "callcenter", + "role": "admin" + }, + { + "do_not_disturb": false, + "group_id": "6422934115149452", + "group_type": "office", + "role": "admin" + } + ], + "id": "5835806903417907", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUYs_jS66r0rgoM.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "international_dialing_enabled": false, + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "6422934115149452", + "on_duty_started": "2013-01-01T00:00:00", + "on_duty_status": "unavailable", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.UserProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- Get", + "tags": [ + "users" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates the provided fields for an existing user.\n\nAdded on March 22, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.update", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.UpdateUserMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "admin_office_ids": [ + "4449170667665453" + ], + "company_id": "4508591728425653", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "Blocky Mockson", + "do_not_disturb": false, + "emails": [ + "bot@fuzz-ball.com" + ], + "extension": "12345", + "first_name": "Blocky", + "forwarding_numbers": [ + "+442074865800", + "+815058098764" + ], + "id": "5835806903417907", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUYs_jS66r0rgoM.png?version=a54c75379069406d7958461d98788fb4", + "international_dialing_enabled": false, + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Junior Accountant", + "language": "en", + "last_name": "Mockson", + "license": "lite_support_agents", + "location": "Mock Location", + "muted": false, + "office_id": "4449170667665453", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.UserProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- Update", + "tags": [ + "users" + ] + } + }, + "/api/v2/users": { + "get": { + "deprecated": false, + "description": "Gets company users, optionally filtering by email.\n\nNOTE: The `limit` parameter has been soft-deprecated. Please omit the `limit` parameter, or reduce it to `100` or less.\n\n- Limit values of greater than `100` will only produce a page size of `100`, and a\n `400 Bad Request` response will be produced 20% of the time in an effort to raise visibility of side-effects that might otherwise go un-noticed by solutions that had assumed a larger page size.\n\n- The `cursor` value is provided in the API response, and can be passed as a parameter to retrieve subsequent pages of results.\n\nAdded on March 22, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "Filter results by the specified user state (e.g. active, suspended, deleted)", + "in": "query", + "name": "state", + "required": false, + "schema": { + "enum": [ + "active", + "all", + "cancelled", + "deleted", + "pending", + "suspended" + ], + "type": "string" + } + }, + { + "description": "If provided, filter results by the specified value to return only company admins or only non-company admins.", + "in": "query", + "name": "company_admin", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "The user's email.", + "in": "query", + "name": "email", + "required": false, + "schema": { + "type": "string" + } + }, + { + "description": "The user's phone number.", + "in": "query", + "name": "number", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "admin_office_ids": [ + "6422934115149452" + ], + "company_id": "4508591728425653", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "\u30c6\u00c9st Bot", + "do_not_disturb": false, + "emails": [ + "bot@fuzz-ball.com" + ], + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "6422934115149452", + "group_type": "office", + "role": "admin" + } + ], + "id": "5835806903417907", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUYs_jS66r0rgoM.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "international_dialing_enabled": false, + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": true, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "6422934115149452", + "phone_numbers": [ + "+14155551001" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + }, + { + "admin_office_ids": [ + "6422934115149452" + ], + "company_id": "4508591728425653", + "country": "us", + "date_active": "2021-06-20T19:18:00", + "date_added": "2021-06-20T19:18:00", + "date_first_login": "2021-06-20T19:18:00", + "display_name": "\u30c6\u00c9st Bot", + "do_not_disturb": false, + "emails": [ + "test_contact@fuzz-ball.com" + ], + "first_name": "\u30c6\u00c9st", + "forwarding_numbers": [ + "+14152301358" + ], + "group_details": [ + { + "do_not_disturb": false, + "group_id": "6422934115149452", + "group_type": "office", + "role": "admin" + } + ], + "id": "4335858969377504", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUY4PWprY3u2QcM.png?version=36c0561fb8e0f5765ccfb3a5316f6d5d", + "international_dialing_enabled": false, + "is_admin": true, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": false, + "job_title": "Mock Job Title", + "language": "en", + "last_name": "Bot", + "license": "talk", + "location": "Mock Location", + "muted": false, + "office_id": "6422934115149452", + "phone_numbers": [ + "+14155551002" + ], + "state": "active", + "status_message": "Mock Status", + "timezone": "US/Pacific" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.UserCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- List", + "tags": [ + "users" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new user.\n\nAdded on March 22, 2018 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.CreateUserMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "company_id": "4508591728425653", + "country": "us", + "date_added": "2013-01-01T00:00:00", + "display_name": "Mocky Mockson", + "do_not_disturb": false, + "emails": [ + "mocky@fuzz-ball.com" + ], + "first_name": "Mocky", + "id": "5137304475768569", + "image_url": "https://dialpad.example.com/avatar/user/ag5kZXZ-dWJlci12b2ljZXIYCxILVXNlclByb2ZpbGUY-a2Gw56LkAkM.png?version=caaf49b4dbcec738c3b8fd1189746b89", + "international_dialing_enabled": false, + "is_admin": false, + "is_available": true, + "is_on_duty": false, + "is_online": false, + "is_super_admin": false, + "language": "en", + "last_name": "Mockson", + "license": "talk", + "muted": false, + "office_id": "6422934115149452", + "state": "active", + "timezone": "US/Pacific" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.UserProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- Create", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/move_office": { + "patch": { + "deprecated": false, + "description": "Moves the user to a different office. For international offices only, all of the user's numbers will be unassigned and a new number will be assigned except when the user only has internal numbers starting with 803 -- then the numbers will remain unchanged. Admin can also assign numbers via the user assign number API after. Only supported on paid accounts and there must be enough licenses to transfer the user to the destination office.\n\nAdded on May 31, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "users.move_office.patch", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.MoveOfficeMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.UserProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User -- Switch Office", + "tags": [ + "users" + ] + } + }, + "/api/v2/users/{id}/status": { + "patch": { + "deprecated": false, + "description": "Update user's status. Returns the user's status if the user exists.\n\nRate limit: 1200 per minute.", + "operationId": "users.update_status", + "parameters": [ + { + "description": "The user's id. ('me' can be used if you are using a user level API key)", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.user.SetStatusMessage", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "expiration": "3600", + "id": "5835806903417907", + "status_message": "test status with expiration" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.user.SetStatusProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "User Status -- Update", + "tags": [ + "users" + ] + } + }, + "/api/v2/webhooks": { + "get": { + "deprecated": false, + "description": "Gets a list of all the webhooks that are associated with the company.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhooks.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "hook_url": "https://test.com/webhooks", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + } + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.webhook.WebhookCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Webhook -- List", + "tags": [ + "webhooks" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new webhook for your company.\n\nAn unique webhook ID will be generated when successfully creating a webhook. A webhook ID is to be required when creating event subscriptions. One webhook ID can be shared between multiple event subscriptions. When triggered, events will be sent to the provided hook_url under webhook. If a secret is provided, the webhook events will be encoded and signed in the JWT format using the shared secret with the HS256 algorithm. The JWT payload should be decoded and the signature verified to ensure that the event came from Dialpad. If no secret is provided, unencoded events will be sent in the JSON format. It is recommended to provide a secret so that you can verify the authenticity of the event.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 100 per minute.", + "operationId": "webhooks.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.webhook.CreateWebhook", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "hook_url": "https://test.com/webhooks", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Webhook -- Create", + "tags": [ + "webhooks" + ] + } + }, + "/api/v2/webhooks/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a webhook by id.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhooks.delete", + "parameters": [ + { + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Webhook -- Delete", + "tags": [ + "webhooks" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a webhook by id.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhooks.get", + "parameters": [ + { + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "hook_url": "https://test.com/webhooks", + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + } + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Webhook -- Get", + "tags": [ + "webhooks" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a webhook by id.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "webhook.update", + "parameters": [ + { + "description": "The webhook's ID, which is generated after creating a webhook successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.webhook.UpdateWebhook", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.webhook.WebhookProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Webhook -- Update", + "tags": [ + "webhooks" + ] + } + }, + "/api/v2/websockets": { + "get": { + "deprecated": false, + "description": "Gets a list of all the websockets that are associated with the company.\n\nAdded on April 5th, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "websockets.list", + "parameters": [ + { + "description": "A token used to return the next page of a previous request. Use the cursor provided in the previous response.", + "in": "query", + "name": "cursor", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "items": [ + { + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + }, + "websocket_url": "wss://platform-websockets-5bwy6zprxa-uc.a.run.app/events/eyJhbGciOiJIUzI1NiIsInR5cCI6InZuZC5kaWFscGFkLndlYnNvY2tldCtqd3QifQ.eyJzdWIiOiJlbmRwb2ludDoxMDAwOioiLCJleHAiOjEzNTcwMDIwMDAsImlzcyI6Imh0dHBzOi8vdGVzdGJlZC5leGFtcGxlLmNvbSIsImlhdCI6MTM1Njk5ODQwMH0.weTDqJj39rODXRkMOC8eci_zjyNlbj8EBS1gzmBy9D4" + } + ] + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketCollection" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Websocket -- List", + "tags": [ + "websockets" + ] + }, + "post": { + "deprecated": false, + "description": "Creates a new websocket for your company.\n\nA unique websocket ID will be generated when successfully creating a websocket. A websocket ID is to be required when creating event subscriptions. One websocket ID can be shared between multiple event subscriptions. When triggered, events will be accessed through provided websocket_url under websocket. The url will be expired after 1 hour. Please use the GET websocket API to regenerate url rather than creating new ones. If a secret is provided, the websocket events will be encoded and signed in the JWT format using the shared secret with the HS256 algorithm. The JWT payload should be decoded and the signature verified to ensure that the event came from Dialpad. If no secret is provided, unencoded events will be sent in the JSON format. It is recommended to provide a secret so that you can verify the authenticity of the event.\n\nAdded on April 5th, 2022 for API v2.\n\nRate limit: 250 per minute.", + "operationId": "websockets.create", + "parameters": [], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.websocket.CreateWebsocket", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + }, + "websocket_url": "wss://platform-websockets-5bwy6zprxa-uc.a.run.app/events/eyJhbGciOiJIUzI1NiIsInR5cCI6InZuZC5kaWFscGFkLndlYnNvY2tldCtqd3QifQ.eyJzdWIiOiJlbmRwb2ludDoxMDAwOioiLCJleHAiOjEzNTcwMDIwMDAsImlzcyI6Imh0dHBzOi8vdGVzdGJlZC5leGFtcGxlLmNvbSIsImlhdCI6MTM1Njk5ODQwMH0.weTDqJj39rODXRkMOC8eci_zjyNlbj8EBS1gzmBy9D4" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Websocket -- Create", + "tags": [ + "websockets" + ] + } + }, + "/api/v2/websockets/{id}": { + "delete": { + "deprecated": false, + "description": "Deletes a websocket by id.\n\nAdded on April 2nd, 2021 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "websockets.delete", + "parameters": [ + { + "description": "The websocket's ID, which is generated after creating a websocket successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Websocket -- Delete", + "tags": [ + "websockets" + ] + }, + "get": { + "deprecated": false, + "description": "Gets a websocket by id.\n\nAdded on April 5th, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "websockets.get", + "parameters": [ + { + "description": "The websocket's ID, which is generated after creating a websocket successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "test_secret", + "type": "jwt" + }, + "websocket_url": "wss://platform-websockets-5bwy6zprxa-uc.a.run.app/events/eyJhbGciOiJIUzI1NiIsInR5cCI6InZuZC5kaWFscGFkLndlYnNvY2tldCtqd3QifQ.eyJzdWIiOiJlbmRwb2ludDoxMDAwOioiLCJleHAiOjEzNTcwMDIwMDAsImlzcyI6Imh0dHBzOi8vdGVzdGJlZC5leGFtcGxlLmNvbSIsImlhdCI6MTM1Njk5ODQwMH0.weTDqJj39rODXRkMOC8eci_zjyNlbj8EBS1gzmBy9D4" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Websocket -- Get", + "tags": [ + "websockets" + ] + }, + "patch": { + "deprecated": false, + "description": "Updates a websocket by id.\n\nAdded on April 5th, 2022 for API v2.\n\nRate limit: 1200 per minute.", + "operationId": "websockets.update", + "parameters": [ + { + "description": "The websocket's ID, which is generated after creating a websocket successfully.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "format": "int64", + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/schemas.websocket.UpdateWebsocket", + "type": "object" + } + } + } + }, + "responses": { + "200": { + "content": { + "application/json": { + "examples": { + "json_example": { + "value": { + "id": "1000", + "signature": { + "algo": "HS256", + "secret": "NEW_SECRET", + "type": "jwt" + }, + "websocket_url": "wss://platform-websockets-5bwy6zprxa-uc.a.run.app/events/eyJhbGciOiJIUzI1NiIsInR5cCI6InZuZC5kaWFscGFkLndlYnNvY2tldCtqd3QifQ.eyJzdWIiOiJlbmRwb2ludDoxMDAwOioiLCJleHAiOjEzNTcwMDIwMDAsImlzcyI6Imh0dHBzOi8vdGVzdGJlZC5leGFtcGxlLmNvbSIsImlhdCI6MTM1Njk5ODQwMH0.weTDqJj39rODXRkMOC8eci_zjyNlbj8EBS1gzmBy9D4" + } + } + }, + "schema": { + "$ref": "#/components/schemas/schemas.websocket.WebsocketProto" + } + } + }, + "description": "A successful response" + } + }, + "summary": "Websocket -- Update", + "tags": [ + "websockets" + ] + } + } + }, + "security": [ + { + "api_key_in_url": [] + }, + { + "bearer_token": [] + } + ], + "servers": [ + { + "url": "https://dialpad.com/" + }, + { + "url": "https://sandbox.dialpad.com/" + } + ], + "x-readme": { + "explorer-enabled": true, + "proxy-enabled": false, + "samples-enabled": true + } +} \ No newline at end of file diff --git a/docs/images/resource_module_mapping.png b/docs/images/resource_module_mapping.png new file mode 100644 index 0000000..c00ac06 Binary files /dev/null and b/docs/images/resource_module_mapping.png differ diff --git a/docs/images/tooltip_example.png b/docs/images/tooltip_example.png new file mode 100644 index 0000000..cba6034 Binary files /dev/null and b/docs/images/tooltip_example.png differ diff --git a/module_mapping.json b/module_mapping.json new file mode 100644 index 0000000..9628aa9 --- /dev/null +++ b/module_mapping.json @@ -0,0 +1,1014 @@ +{ + "/api/v2/numbers/format": { + "post": { + "resource_class": "NumbersResource", + "method_name": "format_number" + } + }, + "/api/v2/accesscontrolpolicies/{id}/assign": { + "post": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "assign" + } + }, + "/api/v2/accesscontrolpolicies": { + "get": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "list" + }, + "post": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "create" + } + }, + "/api/v2/accesscontrolpolicies/{id}": { + "delete": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "delete" + }, + "get": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "get" + }, + "patch": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "partial_update" + } + }, + "/api/v2/accesscontrolpolicies/{id}/assignments": { + "get": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "list_assignments" + } + }, + "/api/v2/accesscontrolpolicies/{id}/unassign": { + "post": { + "resource_class": "AccessControlPoliciesResource", + "method_name": "unassign" + } + }, + "/api/v2/app/settings": { + "get": { + "resource_class": "AppSettingsResource", + "method_name": "get" + } + }, + "/api/v2/blockednumbers/add": { + "post": { + "resource_class": "BlockedNumbersResource", + "method_name": "add" + } + }, + "/api/v2/blockednumbers/{number}": { + "get": { + "resource_class": "BlockedNumbersResource", + "method_name": "get" + } + }, + "/api/v2/blockednumbers/remove": { + "post": { + "resource_class": "BlockedNumbersResource", + "method_name": "remove" + } + }, + "/api/v2/blockednumbers": { + "get": { + "resource_class": "BlockedNumbersResource", + "method_name": "list" + } + }, + "/api/v2/call/{id}/participants/add": { + "post": { + "resource_class": "CallsResource", + "method_name": "add_participant" + } + }, + "/api/v2/call/{id}": { + "get": { + "resource_class": "CallsResource", + "method_name": "get" + } + }, + "/api/v2/call/initiate_ivr_call": { + "post": { + "resource_class": "CallsResource", + "method_name": "initiate_ivr_call" + } + }, + "/api/v2/call": { + "get": { + "resource_class": "CallsResource", + "method_name": "list" + }, + "post": { + "resource_class": "CallsResource", + "method_name": "initiate_ring_call" + } + }, + "/api/v2/call/{id}/transfer": { + "post": { + "resource_class": "CallsResource", + "method_name": "transfer" + } + }, + "/api/v2/call/{id}/unpark": { + "post": { + "resource_class": "CallsResource", + "method_name": "unpark" + } + }, + "/api/v2/call/{id}/actions/hangup": { + "put": { + "resource_class": "CallsResource", + "method_name": "hangup_call" + } + }, + "/api/v2/call/{id}/labels": { + "put": { + "resource_class": "CallsResource", + "method_name": "set_call_label" + } + }, + "/api/v2/callback": { + "post": { + "resource_class": "CallbacksResource", + "method_name": "enqueue_callback" + } + }, + "/api/v2/callback/validate": { + "post": { + "resource_class": "CallbacksResource", + "method_name": "validate_callback" + } + }, + "/api/v2/callcenters": { + "get": { + "resource_class": "CallCentersResource", + "method_name": "list" + }, + "post": { + "resource_class": "CallCentersResource", + "method_name": "create" + } + }, + "/api/v2/callcenters/{id}": { + "delete": { + "resource_class": "CallCentersResource", + "method_name": "delete" + }, + "get": { + "resource_class": "CallCentersResource", + "method_name": "get" + }, + "patch": { + "resource_class": "CallCentersResource", + "method_name": "partial_update" + } + }, + "/api/v2/callcenters/{id}/status": { + "get": { + "resource_class": "CallCentersResource", + "method_name": "get_status" + } + }, + "/api/v2/callcenters/operators/{id}/dutystatus": { + "get": { + "resource_class": "CallCenterOperatorsResource", + "method_name": "get_duty_status" + }, + "patch": { + "resource_class": "CallCenterOperatorsResource", + "method_name": "update_duty_status" + } + }, + "/api/v2/callcenters/{call_center_id}/operators/{user_id}/skill": { + "get": { + "resource_class": "CallCentersResource", + "method_name": "get_operator_skill_level" + }, + "patch": { + "resource_class": "CallCentersResource", + "method_name": "update_operator_skill_level" + } + }, + "/api/v2/callcenters/{id}/operators": { + "delete": { + "resource_class": "CallCentersResource", + "method_name": "remove_operator" + }, + "get": { + "resource_class": "CallCentersResource", + "method_name": "list_operators" + }, + "post": { + "resource_class": "CallCentersResource", + "method_name": "add_operator" + } + }, + "/api/v2/calllabels": { + "get": { + "resource_class": "CallLabelsResource", + "method_name": "list" + } + }, + "/api/v2/callreviewsharelink": { + "post": { + "resource_class": "CallReviewShareLinksResource", + "method_name": "create" + } + }, + "/api/v2/callreviewsharelink/{id}": { + "delete": { + "resource_class": "CallReviewShareLinksResource", + "method_name": "delete" + }, + "get": { + "resource_class": "CallReviewShareLinksResource", + "method_name": "get" + }, + "put": { + "resource_class": "CallReviewShareLinksResource", + "method_name": "update" + } + }, + "/api/v2/callrouters": { + "get": { + "resource_class": "CallRoutersResource", + "method_name": "list" + }, + "post": { + "resource_class": "CallRoutersResource", + "method_name": "create" + } + }, + "/api/v2/callrouters/{id}": { + "delete": { + "resource_class": "CallRoutersResource", + "method_name": "delete" + }, + "get": { + "resource_class": "CallRoutersResource", + "method_name": "get" + }, + "patch": { + "resource_class": "CallRoutersResource", + "method_name": "partial_update" + } + }, + "/api/v2/callrouters/{id}/assign_number": { + "post": { + "resource_class": "CallRoutersResource", + "method_name": "assign_number" + } + }, + "/api/v2/channels/{id}": { + "delete": { + "resource_class": "ChannelsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "ChannelsResource", + "method_name": "get" + } + }, + "/api/v2/channels": { + "get": { + "resource_class": "ChannelsResource", + "method_name": "list" + }, + "post": { + "resource_class": "ChannelsResource", + "method_name": "create" + } + }, + "/api/v2/channels/{id}/members": { + "delete": { + "resource_class": "ChannelsResource", + "method_name": "remove_member" + }, + "get": { + "resource_class": "ChannelsResource", + "method_name": "list_members" + }, + "post": { + "resource_class": "ChannelsResource", + "method_name": "add_member" + } + }, + "/api/v2/coachingteams/{id}/members": { + "get": { + "resource_class": "CoachingTeamsResource", + "method_name": "list_members" + }, + "post": { + "resource_class": "CoachingTeamsResource", + "method_name": "add_member" + } + }, + "/api/v2/coachingteams/{id}": { + "get": { + "resource_class": "CoachingTeamsResource", + "method_name": "get" + } + }, + "/api/v2/coachingteams": { + "get": { + "resource_class": "CoachingTeamsResource", + "method_name": "list" + } + }, + "/api/v2/company": { + "get": { + "resource_class": "CompanyResource", + "method_name": "get" + } + }, + "/api/v2/company/{id}/smsoptout": { + "get": { + "resource_class": "CompanyResource", + "method_name": "get_sms_opt_out_list" + } + }, + "/api/v2/conference/rooms": { + "get": { + "resource_class": "MeetingRoomsResource", + "method_name": "list" + } + }, + "/api/v2/conference/meetings": { + "get": { + "resource_class": "MeetingsResource", + "method_name": "list" + } + }, + "/api/v2/contacts/{id}": { + "delete": { + "resource_class": "ContactsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "ContactsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "ContactsResource", + "method_name": "partial_update" + } + }, + "/api/v2/contacts": { + "get": { + "resource_class": "ContactsResource", + "method_name": "list" + }, + "post": { + "resource_class": "ContactsResource", + "method_name": "create" + }, + "put": { + "resource_class": "ContactsResource", + "method_name": "create_or_update" + } + }, + "/api/v2/customivrs/{target_type}/{target_id}/{ivr_type}": { + "delete": { + "resource_class": "CustomIVRsResource", + "method_name": "unassign" + }, + "patch": { + "resource_class": "CustomIVRsResource", + "method_name": "assign" + } + }, + "/api/v2/customivrs": { + "get": { + "resource_class": "CustomIVRsResource", + "method_name": "list" + }, + "post": { + "resource_class": "CustomIVRsResource", + "method_name": "create" + } + }, + "/api/v2/customivrs/{ivr_id}": { + "patch": { + "resource_class": "CustomIVRsResource", + "method_name": "partial_update" + } + }, + "/api/v2/departments/{id}": { + "delete": { + "resource_class": "DepartmentsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "DepartmentsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "DepartmentsResource", + "method_name": "partial_update" + } + }, + "/api/v2/departments": { + "get": { + "resource_class": "DepartmentsResource", + "method_name": "list" + }, + "post": { + "resource_class": "DepartmentsResource", + "method_name": "create" + } + }, + "/api/v2/departments/{id}/operators": { + "delete": { + "resource_class": "DepartmentsResource", + "method_name": "remove_operator" + }, + "get": { + "resource_class": "DepartmentsResource", + "method_name": "list_operators" + }, + "post": { + "resource_class": "DepartmentsResource", + "method_name": "add_operator" + } + }, + "/api/v2/faxline": { + "post": { + "resource_class": "FaxLinesResource", + "method_name": "assign" + } + }, + "/api/v2/numbers/{number}/assign": { + "post": { + "resource_class": "NumbersResource", + "method_name": "assign" + } + }, + "/api/v2/numbers/assign": { + "post": { + "resource_class": "NumbersResource", + "method_name": "auto_assign" + } + }, + "/api/v2/numbers/{number}": { + "delete": { + "resource_class": "NumbersResource", + "method_name": "unassign" + }, + "get": { + "resource_class": "NumbersResource", + "method_name": "get" + } + }, + "/api/v2/numbers": { + "get": { + "resource_class": "NumbersResource", + "method_name": "list" + } + }, + "/api/v2/numbers/swap": { + "post": { + "resource_class": "NumbersResource", + "method_name": "swap" + } + }, + "/oauth2/authorize": { + "get": { + "resource_class": "OAuth2Resource", + "method_name": "authorize_token" + } + }, + "/oauth2/deauthorize": { + "post": { + "resource_class": "OAuth2Resource", + "method_name": "deauthorize_token" + } + }, + "/oauth2/token": { + "post": { + "resource_class": "OAuth2Resource", + "method_name": "redeem_token" + } + }, + "/api/v2/offices/{office_id}/plan": { + "get": { + "resource_class": "OfficesResource", + "method_name": "get_billing_plan" + } + }, + "/api/v2/offices/{office_id}/callcenters": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list_call_centers" + } + }, + "/api/v2/offices/{office_id}/teams": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list_coaching_teams" + } + }, + "/api/v2/offices/{office_id}/departments": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list_departments" + } + }, + "/api/v2/offices/{id}/assign_number": { + "post": { + "resource_class": "OfficesResource", + "method_name": "assign_number" + } + }, + "/api/v2/offices/{id}/unassign_number": { + "post": { + "resource_class": "OfficesResource", + "method_name": "unassign_number" + } + }, + "/api/v2/offices/{id}/e911": { + "get": { + "resource_class": "OfficesResource", + "method_name": "get_e911_address" + }, + "put": { + "resource_class": "OfficesResource", + "method_name": "update_e911_address" + } + }, + "/api/v2/offices/{office_id}/available_licenses": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list_available_licenses" + } + }, + "/api/v2/offices/{id}/offdutystatuses": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list_offduty_statuses" + } + }, + "/api/v2/offices/{id}": { + "get": { + "resource_class": "OfficesResource", + "method_name": "get" + } + }, + "/api/v2/offices": { + "get": { + "resource_class": "OfficesResource", + "method_name": "list" + }, + "post": { + "resource_class": "OfficesResource", + "method_name": "create" + } + }, + "/api/v2/offices/{id}/operators": { + "delete": { + "resource_class": "OfficesResource", + "method_name": "remove_operator" + }, + "get": { + "resource_class": "OfficesResource", + "method_name": "list_operators" + }, + "post": { + "resource_class": "OfficesResource", + "method_name": "add_operator" + } + }, + "/api/v2/recordingsharelink": { + "post": { + "resource_class": "RecordingShareLinksResource", + "method_name": "create" + } + }, + "/api/v2/recordingsharelink/{id}": { + "delete": { + "resource_class": "RecordingShareLinksResource", + "method_name": "delete" + }, + "get": { + "resource_class": "RecordingShareLinksResource", + "method_name": "get" + }, + "put": { + "resource_class": "RecordingShareLinksResource", + "method_name": "update" + } + }, + "/api/v2/rooms/{id}/assign_number": { + "post": { + "resource_class": "RoomsResource", + "method_name": "assign_number" + } + }, + "/api/v2/rooms/{id}/unassign_number": { + "post": { + "resource_class": "RoomsResource", + "method_name": "unassign_number" + } + }, + "/api/v2/rooms/{id}": { + "delete": { + "resource_class": "RoomsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "RoomsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "RoomsResource", + "method_name": "partial_update" + } + }, + "/api/v2/rooms": { + "get": { + "resource_class": "RoomsResource", + "method_name": "list" + }, + "post": { + "resource_class": "RoomsResource", + "method_name": "create" + } + }, + "/api/v2/rooms/international_pin": { + "post": { + "resource_class": "RoomsResource", + "method_name": "assign_phone_pin" + } + }, + "/api/v2/rooms/{parent_id}/deskphones/{id}": { + "delete": { + "resource_class": "RoomsResource", + "method_name": "delete_room_phone" + }, + "get": { + "resource_class": "RoomsResource", + "method_name": "get_room_phone" + } + }, + "/api/v2/rooms/{parent_id}/deskphones": { + "get": { + "resource_class": "RoomsResource", + "method_name": "list_room_phones" + } + }, + "/api/v2/schedulereports/{id}": { + "delete": { + "resource_class": "ScheduleReportsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "ScheduleReportsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "ScheduleReportsResource", + "method_name": "partial_update" + } + }, + "/api/v2/schedulereports": { + "get": { + "resource_class": "ScheduleReportsResource", + "method_name": "list" + }, + "post": { + "resource_class": "ScheduleReportsResource", + "method_name": "create" + } + }, + "/api/v2/sms": { + "post": { + "resource_class": "SmsResource", + "method_name": "send" + } + }, + "/api/v2/stats/{id}": { + "get": { + "resource_class": "StatsResource", + "method_name": "get_result" + } + }, + "/api/v2/stats": { + "post": { + "resource_class": "StatsResource", + "method_name": "initiate_processing" + } + }, + "/api/v2/subscriptions/agent_status": { + "get": { + "resource_class": "AgentStatusEventSubscriptionsResource", + "method_name": "list" + }, + "post": { + "resource_class": "AgentStatusEventSubscriptionsResource", + "method_name": "create" + } + }, + "/api/v2/subscriptions/agent_status/{id}": { + "delete": { + "resource_class": "AgentStatusEventSubscriptionsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "AgentStatusEventSubscriptionsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "AgentStatusEventSubscriptionsResource", + "method_name": "partial_update" + } + }, + "/api/v2/subscriptions/call": { + "get": { + "resource_class": "CallEventSubscriptionsResource", + "method_name": "list" + }, + "post": { + "resource_class": "CallEventSubscriptionsResource", + "method_name": "create" + } + }, + "/api/v2/subscriptions/call/{id}": { + "delete": { + "resource_class": "CallEventSubscriptionsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "CallEventSubscriptionsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "CallEventSubscriptionsResource", + "method_name": "partial_update" + } + }, + "/api/v2/subscriptions/changelog": { + "get": { + "resource_class": "ChangelogEventSubscriptionsResource", + "method_name": "list" + }, + "post": { + "resource_class": "ChangelogEventSubscriptionsResource", + "method_name": "create" + } + }, + "/api/v2/subscriptions/changelog/{id}": { + "delete": { + "resource_class": "ChangelogEventSubscriptionsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "ChangelogEventSubscriptionsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "ChangelogEventSubscriptionsResource", + "method_name": "partial_update" + } + }, + "/api/v2/subscriptions/contact": { + "get": { + "resource_class": "ContactEventSubscriptionsResource", + "method_name": "list" + }, + "post": { + "resource_class": "ContactEventSubscriptionsResource", + "method_name": "create" + } + }, + "/api/v2/subscriptions/contact/{id}": { + "delete": { + "resource_class": "ContactEventSubscriptionsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "ContactEventSubscriptionsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "ContactEventSubscriptionsResource", + "method_name": "partial_update" + } + }, + "/api/v2/subscriptions/sms": { + "get": { + "resource_class": "SmsEventSubscriptionsResource", + "method_name": "list" + }, + "post": { + "resource_class": "SmsEventSubscriptionsResource", + "method_name": "create" + } + }, + "/api/v2/subscriptions/sms/{id}": { + "delete": { + "resource_class": "SmsEventSubscriptionsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "SmsEventSubscriptionsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "SmsEventSubscriptionsResource", + "method_name": "partial_update" + } + }, + "/api/v2/transcripts/{call_id}": { + "get": { + "resource_class": "TranscriptsResource", + "method_name": "get" + } + }, + "/api/v2/transcripts/{call_id}/url": { + "get": { + "resource_class": "TranscriptsResource", + "method_name": "get_url" + } + }, + "/api/v2/userdevices/{id}": { + "get": { + "resource_class": "UserDevicesResource", + "method_name": "get" + } + }, + "/api/v2/userdevices": { + "get": { + "resource_class": "UserDevicesResource", + "method_name": "list" + } + }, + "/api/v2/users/{id}/initiate_call": { + "post": { + "resource_class": "UsersResource", + "method_name": "initiate_call" + } + }, + "/api/v2/users/{id}/activecall": { + "patch": { + "resource_class": "UsersResource", + "method_name": "toggle_active_call_recording" + } + }, + "/api/v2/users/{id}/togglevi": { + "patch": { + "resource_class": "UsersResource", + "method_name": "toggle_active_call_vi" + } + }, + "/api/v2/users/{id}/caller_id": { + "get": { + "resource_class": "UsersResource", + "method_name": "get_caller_id" + }, + "post": { + "resource_class": "UsersResource", + "method_name": "set_caller_id" + } + }, + "/api/v2/users/{parent_id}/deskphones/{id}": { + "delete": { + "resource_class": "UsersResource", + "method_name": "delete_deskphone" + }, + "get": { + "resource_class": "UsersResource", + "method_name": "get_deskphone" + } + }, + "/api/v2/users/{parent_id}/deskphones": { + "get": { + "resource_class": "UsersResource", + "method_name": "list_deskphones" + } + }, + "/api/v2/users/{id}/assign_number": { + "post": { + "resource_class": "UsersResource", + "method_name": "assign_number" + } + }, + "/api/v2/users/{id}/unassign_number": { + "post": { + "resource_class": "UsersResource", + "method_name": "unassign_number" + } + }, + "/api/v2/users/{id}/togglednd": { + "patch": { + "resource_class": "UsersResource", + "method_name": "toggle_dnd" + } + }, + "/api/v2/users/{id}/e911": { + "get": { + "resource_class": "UsersResource", + "method_name": "get_e911_address" + }, + "put": { + "resource_class": "UsersResource", + "method_name": "set_e911_address" + } + }, + "/api/v2/users/{id}/personas": { + "get": { + "resource_class": "UsersResource", + "method_name": "list_personas" + } + }, + "/api/v2/users/{id}/screenpop": { + "post": { + "resource_class": "UsersResource", + "method_name": "trigger_screenpop" + } + }, + "/api/v2/users/{id}": { + "delete": { + "resource_class": "UsersResource", + "method_name": "delete" + }, + "get": { + "resource_class": "UsersResource", + "method_name": "get" + }, + "patch": { + "resource_class": "UsersResource", + "method_name": "partial_update" + } + }, + "/api/v2/users": { + "get": { + "resource_class": "UsersResource", + "method_name": "list" + }, + "post": { + "resource_class": "UsersResource", + "method_name": "create" + } + }, + "/api/v2/users/{id}/move_office": { + "patch": { + "resource_class": "UsersResource", + "method_name": "move_office" + } + }, + "/api/v2/users/{id}/status": { + "patch": { + "resource_class": "UsersResource", + "method_name": "update_user_status" + } + }, + "/api/v2/webhooks": { + "get": { + "resource_class": "WebhooksResource", + "method_name": "list" + }, + "post": { + "resource_class": "WebhooksResource", + "method_name": "create" + } + }, + "/api/v2/webhooks/{id}": { + "delete": { + "resource_class": "WebhooksResource", + "method_name": "delete" + }, + "get": { + "resource_class": "WebhooksResource", + "method_name": "get" + }, + "patch": { + "resource_class": "WebhooksResource", + "method_name": "partial_update" + } + }, + "/api/v2/websockets": { + "get": { + "resource_class": "WebsocketsResource", + "method_name": "list" + }, + "post": { + "resource_class": "WebsocketsResource", + "method_name": "create" + } + }, + "/api/v2/websockets/{id}": { + "delete": { + "resource_class": "WebsocketsResource", + "method_name": "delete" + }, + "get": { + "resource_class": "WebsocketsResource", + "method_name": "get" + }, + "patch": { + "resource_class": "WebsocketsResource", + "method_name": "partial_update" + } + } +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..55c5e5e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,67 @@ +[project] +name = "python-dialpad" +version = "3.0.0" +description = "A python wrapper for the Dialpad REST API" +readme = "README.md" +requires-python = ">=3.9" +dependencies = [ + "cached-property>=2.0.1", + "requests>=2.28.0", +] + +[project.scripts] +cli = "cli.main:app" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["src/dialpad"] + +[tool.pytest.ini_options] +testpaths = ["tests"] +pythonpath = ["src"] +addopts = "-xs --showlocals --cov=dialpad --cov-fail-under=95" + +[tool.uv] +dev-dependencies = [ + "faker>=37.3.0", + "inquirer>=3.4.0", + "openapi-core>=0.19.5", + "pytest-cov>=6.2.1", + "pytest>=8.4.0", + "requests-mock>=1.12.1", + "ruff>=0.11.12", + "six>=1.17.0", + "swagger-parser", + "swagger-stub>=0.2.1", + "typer>=0.16.0", + "urllib3>=2.4.0", +] + +[tool.uv.sources] +swagger-parser = { git = "https://github.com/jakedialpad/swagger-parser", rev = "v1.0.1b" } + +[tool.ruff] +line-length = 100 +indent-width = 2 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort +] +ignore = ["E501"] # line-too-long + +[tool.ruff.format] +quote-style = "single" +indent-style = "space" +docstring-code-format = true + +[tool.ruff.lint.isort] +known-first-party = ["dialpad", "cli"] +force-single-line = false +split-on-trailing-comma = true diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a87624d..0000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -cached-property == 1.5.1 -certifi == 2020.6.20 -chardet == 3.0.4 -idna == 2.10 -requests == 2.24.0 -urllib3 == 1.25.10 diff --git a/setup.py b/setup.py deleted file mode 100644 index 63644ba..0000000 --- a/setup.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- - -from setuptools import setup, find_packages - - -def readme(): - with open('README.md') as f: - return f.read() - - -setup( - name='python-dialpad', - version='2.2.3', - description='A python wrapper for the Dialpad REST API', - long_description=readme(), - long_description_content_type="text/markdown", - author='Jake Nielsen', - author_email='jnielsen@dialpad.com', - license='MIT', - url='https://github.com/dialpad/dialpad-python-sdk', - install_requires=[ - 'cached-property', - 'certifi', - 'chardet', - 'idna', - 'requests', - 'urllib3' - ], - include_package_data=True, - packages=find_packages() -) diff --git a/dialpad/__init__.py b/src/dialpad/__init__.py similarity index 50% rename from dialpad/__init__.py rename to src/dialpad/__init__.py index 2b5f572..9624e90 100644 --- a/dialpad/__init__.py +++ b/src/dialpad/__init__.py @@ -1 +1,5 @@ from .client import DialpadClient + +__all__ = [ + 'DialpadClient', +] diff --git a/src/dialpad/client.py b/src/dialpad/client.py new file mode 100644 index 0000000..6245e16 --- /dev/null +++ b/src/dialpad/client.py @@ -0,0 +1,112 @@ +from typing import Iterator, Optional + +import requests + +from .resources import DialpadResourcesMixin + +hosts = dict(live='https://dialpad.com', sandbox='https://sandbox.dialpad.com') + + +class DialpadClient(DialpadResourcesMixin): + def __init__( + self, + token: str, + sandbox: bool = False, + base_url: Optional[str] = None, + company_id: Optional[str] = None, + ): + self._token = token + self._session = requests.Session() + self._base_url = base_url or hosts.get('sandbox' if sandbox else 'live') + self._company_id = company_id + + @property + def company_id(self): + return self._company_id + + @company_id.setter + def company_id(self, value): + self._company_id = value + + @company_id.deleter + def company_id(self): + del self._company_id + + def _url(self, path: str) -> str: + return f'{self._base_url}/{path.lstrip("/")}' + + def _raw_request( + self, + method: str = 'GET', + sub_path: Optional[str] = None, + params: Optional[dict] = None, + body: Optional[dict] = None, + headers: Optional[dict] = None, + ) -> requests.Response: + url = self._url(sub_path) + headers = headers or dict() + if self.company_id: + headers.update({'DP-Company-ID': str(self.company_id)}) + + headers.update({'Authorization': 'Bearer %s' % self._token}) + if str(method).upper() in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']: + return getattr(self._session, str(method).lower())( + url, + headers=headers, + params=params, + json=body, + ) + raise ValueError('Unsupported method "%s"' % method) + + def iter_request( + self, + method: str = 'GET', + sub_path: Optional[str] = None, + params: Optional[dict] = None, + body: Optional[dict] = None, + headers: Optional[dict] = None, + ) -> Iterator[dict]: + # Ensure that we have a mutable copy of params. + params = dict(params or {}) + response = self._raw_request( + method=method, sub_path=sub_path, params=params, body=body, headers=headers + ) + response.raise_for_status() + + if response.status_code == 204: # No Content + return + + response_json = response.json() + if 'items' in response_json: + yield from (response_json['items'] or []) + + while response_json.get('cursor', None): + params['cursor'] = response_json['cursor'] + response = self._raw_request( + method=method, sub_path=sub_path, params=params, body=body, headers=headers + ) + response.raise_for_status() + if response.status_code == 204: # No Content + return + + response_json = response.json() + if 'items' in response_json: + yield from (response_json['items'] or []) + + def request( + self, + method: str = 'GET', + sub_path: Optional[str] = None, + params: Optional[dict] = None, + body: Optional[dict] = None, + headers: Optional[dict] = None, + ) -> dict: + response = self._raw_request( + method=method, sub_path=sub_path, params=params, body=body, headers=headers + ) + response.raise_for_status() + + if response.status_code == 204: # No Content + return None + + return response.json() diff --git a/src/dialpad/py.typed b/src/dialpad/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/dialpad/resources/__init__.py b/src/dialpad/resources/__init__.py new file mode 100644 index 0000000..b723991 --- /dev/null +++ b/src/dialpad/resources/__init__.py @@ -0,0 +1,422 @@ +# This is an auto-generated resource package. Please do not edit it directly. + +from .access_control_policies_resource import AccessControlPoliciesResource +from .agent_status_event_subscriptions_resource import AgentStatusEventSubscriptionsResource +from .app_settings_resource import AppSettingsResource +from .blocked_numbers_resource import BlockedNumbersResource +from .call_center_operators_resource import CallCenterOperatorsResource +from .call_centers_resource import CallCentersResource +from .call_event_subscriptions_resource import CallEventSubscriptionsResource +from .call_labels_resource import CallLabelsResource +from .call_review_share_links_resource import CallReviewShareLinksResource +from .call_routers_resource import CallRoutersResource +from .callbacks_resource import CallbacksResource +from .calls_resource import CallsResource +from .changelog_event_subscriptions_resource import ChangelogEventSubscriptionsResource +from .channels_resource import ChannelsResource +from .coaching_teams_resource import CoachingTeamsResource +from .company_resource import CompanyResource +from .contact_event_subscriptions_resource import ContactEventSubscriptionsResource +from .contacts_resource import ContactsResource +from .custom_ivrs_resource import CustomIVRsResource +from .departments_resource import DepartmentsResource +from .fax_lines_resource import FaxLinesResource +from .meeting_rooms_resource import MeetingRoomsResource +from .meetings_resource import MeetingsResource +from .numbers_resource import NumbersResource +from .oauth2_resource import OAuth2Resource +from .offices_resource import OfficesResource +from .recording_share_links_resource import RecordingShareLinksResource +from .rooms_resource import RoomsResource +from .schedule_reports_resource import ScheduleReportsResource +from .sms_event_subscriptions_resource import SmsEventSubscriptionsResource +from .sms_resource import SmsResource +from .stats_resource import StatsResource +from .transcripts_resource import TranscriptsResource +from .user_devices_resource import UserDevicesResource +from .users_resource import UsersResource +from .webhooks_resource import WebhooksResource +from .websockets_resource import WebsocketsResource + + +class DialpadResourcesMixin: + """Mixin class that provides resource properties for each API resource. + + This mixin is used by the DialpadClient class to provide easy access + to all API resources as properties. + """ + + @property + def access_control_policies(self) -> AccessControlPoliciesResource: + """Returns an instance of AccessControlPoliciesResource. + + Returns: + A AccessControlPoliciesResource instance initialized with this client. + """ + return AccessControlPoliciesResource(self) + + @property + def agent_status_event_subscriptions(self) -> AgentStatusEventSubscriptionsResource: + """Returns an instance of AgentStatusEventSubscriptionsResource. + + Returns: + A AgentStatusEventSubscriptionsResource instance initialized with this client. + """ + return AgentStatusEventSubscriptionsResource(self) + + @property + def app_settings(self) -> AppSettingsResource: + """Returns an instance of AppSettingsResource. + + Returns: + A AppSettingsResource instance initialized with this client. + """ + return AppSettingsResource(self) + + @property + def blocked_numbers(self) -> BlockedNumbersResource: + """Returns an instance of BlockedNumbersResource. + + Returns: + A BlockedNumbersResource instance initialized with this client. + """ + return BlockedNumbersResource(self) + + @property + def call_center_operators(self) -> CallCenterOperatorsResource: + """Returns an instance of CallCenterOperatorsResource. + + Returns: + A CallCenterOperatorsResource instance initialized with this client. + """ + return CallCenterOperatorsResource(self) + + @property + def call_centers(self) -> CallCentersResource: + """Returns an instance of CallCentersResource. + + Returns: + A CallCentersResource instance initialized with this client. + """ + return CallCentersResource(self) + + @property + def call_event_subscriptions(self) -> CallEventSubscriptionsResource: + """Returns an instance of CallEventSubscriptionsResource. + + Returns: + A CallEventSubscriptionsResource instance initialized with this client. + """ + return CallEventSubscriptionsResource(self) + + @property + def call_labels(self) -> CallLabelsResource: + """Returns an instance of CallLabelsResource. + + Returns: + A CallLabelsResource instance initialized with this client. + """ + return CallLabelsResource(self) + + @property + def call_review_share_links(self) -> CallReviewShareLinksResource: + """Returns an instance of CallReviewShareLinksResource. + + Returns: + A CallReviewShareLinksResource instance initialized with this client. + """ + return CallReviewShareLinksResource(self) + + @property + def call_routers(self) -> CallRoutersResource: + """Returns an instance of CallRoutersResource. + + Returns: + A CallRoutersResource instance initialized with this client. + """ + return CallRoutersResource(self) + + @property + def callbacks(self) -> CallbacksResource: + """Returns an instance of CallbacksResource. + + Returns: + A CallbacksResource instance initialized with this client. + """ + return CallbacksResource(self) + + @property + def calls(self) -> CallsResource: + """Returns an instance of CallsResource. + + Returns: + A CallsResource instance initialized with this client. + """ + return CallsResource(self) + + @property + def changelog_event_subscriptions(self) -> ChangelogEventSubscriptionsResource: + """Returns an instance of ChangelogEventSubscriptionsResource. + + Returns: + A ChangelogEventSubscriptionsResource instance initialized with this client. + """ + return ChangelogEventSubscriptionsResource(self) + + @property + def channels(self) -> ChannelsResource: + """Returns an instance of ChannelsResource. + + Returns: + A ChannelsResource instance initialized with this client. + """ + return ChannelsResource(self) + + @property + def coaching_teams(self) -> CoachingTeamsResource: + """Returns an instance of CoachingTeamsResource. + + Returns: + A CoachingTeamsResource instance initialized with this client. + """ + return CoachingTeamsResource(self) + + @property + def company(self) -> CompanyResource: + """Returns an instance of CompanyResource. + + Returns: + A CompanyResource instance initialized with this client. + """ + return CompanyResource(self) + + @property + def contact_event_subscriptions(self) -> ContactEventSubscriptionsResource: + """Returns an instance of ContactEventSubscriptionsResource. + + Returns: + A ContactEventSubscriptionsResource instance initialized with this client. + """ + return ContactEventSubscriptionsResource(self) + + @property + def contacts(self) -> ContactsResource: + """Returns an instance of ContactsResource. + + Returns: + A ContactsResource instance initialized with this client. + """ + return ContactsResource(self) + + @property + def custom_ivrs(self) -> CustomIVRsResource: + """Returns an instance of CustomIVRsResource. + + Returns: + A CustomIVRsResource instance initialized with this client. + """ + return CustomIVRsResource(self) + + @property + def departments(self) -> DepartmentsResource: + """Returns an instance of DepartmentsResource. + + Returns: + A DepartmentsResource instance initialized with this client. + """ + return DepartmentsResource(self) + + @property + def fax_lines(self) -> FaxLinesResource: + """Returns an instance of FaxLinesResource. + + Returns: + A FaxLinesResource instance initialized with this client. + """ + return FaxLinesResource(self) + + @property + def meeting_rooms(self) -> MeetingRoomsResource: + """Returns an instance of MeetingRoomsResource. + + Returns: + A MeetingRoomsResource instance initialized with this client. + """ + return MeetingRoomsResource(self) + + @property + def meetings(self) -> MeetingsResource: + """Returns an instance of MeetingsResource. + + Returns: + A MeetingsResource instance initialized with this client. + """ + return MeetingsResource(self) + + @property + def numbers(self) -> NumbersResource: + """Returns an instance of NumbersResource. + + Returns: + A NumbersResource instance initialized with this client. + """ + return NumbersResource(self) + + @property + def oauth2(self) -> OAuth2Resource: + """Returns an instance of OAuth2Resource. + + Returns: + A OAuth2Resource instance initialized with this client. + """ + return OAuth2Resource(self) + + @property + def offices(self) -> OfficesResource: + """Returns an instance of OfficesResource. + + Returns: + A OfficesResource instance initialized with this client. + """ + return OfficesResource(self) + + @property + def recording_share_links(self) -> RecordingShareLinksResource: + """Returns an instance of RecordingShareLinksResource. + + Returns: + A RecordingShareLinksResource instance initialized with this client. + """ + return RecordingShareLinksResource(self) + + @property + def rooms(self) -> RoomsResource: + """Returns an instance of RoomsResource. + + Returns: + A RoomsResource instance initialized with this client. + """ + return RoomsResource(self) + + @property + def schedule_reports(self) -> ScheduleReportsResource: + """Returns an instance of ScheduleReportsResource. + + Returns: + A ScheduleReportsResource instance initialized with this client. + """ + return ScheduleReportsResource(self) + + @property + def sms_event_subscriptions(self) -> SmsEventSubscriptionsResource: + """Returns an instance of SmsEventSubscriptionsResource. + + Returns: + A SmsEventSubscriptionsResource instance initialized with this client. + """ + return SmsEventSubscriptionsResource(self) + + @property + def sms(self) -> SmsResource: + """Returns an instance of SmsResource. + + Returns: + A SmsResource instance initialized with this client. + """ + return SmsResource(self) + + @property + def stats(self) -> StatsResource: + """Returns an instance of StatsResource. + + Returns: + A StatsResource instance initialized with this client. + """ + return StatsResource(self) + + @property + def transcripts(self) -> TranscriptsResource: + """Returns an instance of TranscriptsResource. + + Returns: + A TranscriptsResource instance initialized with this client. + """ + return TranscriptsResource(self) + + @property + def user_devices(self) -> UserDevicesResource: + """Returns an instance of UserDevicesResource. + + Returns: + A UserDevicesResource instance initialized with this client. + """ + return UserDevicesResource(self) + + @property + def users(self) -> UsersResource: + """Returns an instance of UsersResource. + + Returns: + A UsersResource instance initialized with this client. + """ + return UsersResource(self) + + @property + def webhooks(self) -> WebhooksResource: + """Returns an instance of WebhooksResource. + + Returns: + A WebhooksResource instance initialized with this client. + """ + return WebhooksResource(self) + + @property + def websockets(self) -> WebsocketsResource: + """Returns an instance of WebsocketsResource. + + Returns: + A WebsocketsResource instance initialized with this client. + """ + return WebsocketsResource(self) + + +__all__ = [ + 'AccessControlPoliciesResource', + 'AgentStatusEventSubscriptionsResource', + 'AppSettingsResource', + 'BlockedNumbersResource', + 'CallCenterOperatorsResource', + 'CallCentersResource', + 'CallEventSubscriptionsResource', + 'CallLabelsResource', + 'CallReviewShareLinksResource', + 'CallRoutersResource', + 'CallbacksResource', + 'CallsResource', + 'ChangelogEventSubscriptionsResource', + 'ChannelsResource', + 'CoachingTeamsResource', + 'CompanyResource', + 'ContactEventSubscriptionsResource', + 'ContactsResource', + 'CustomIVRsResource', + 'DepartmentsResource', + 'FaxLinesResource', + 'MeetingRoomsResource', + 'MeetingsResource', + 'NumbersResource', + 'OAuth2Resource', + 'OfficesResource', + 'RecordingShareLinksResource', + 'RoomsResource', + 'ScheduleReportsResource', + 'SmsEventSubscriptionsResource', + 'SmsResource', + 'StatsResource', + 'TranscriptsResource', + 'UserDevicesResource', + 'UsersResource', + 'WebhooksResource', + 'WebsocketsResource', + 'DialpadResourcesMixin', +] diff --git a/src/dialpad/resources/access_control_policies_resource.py b/src/dialpad/resources/access_control_policies_resource.py new file mode 100644 index 0000000..55021da --- /dev/null +++ b/src/dialpad/resources/access_control_policies_resource.py @@ -0,0 +1,162 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.access_control_policies import ( + AssignmentPolicyMessage, + CreatePolicyMessage, + PolicyAssignmentProto, + PolicyProto, + UnassignmentPolicyMessage, + UpdatePolicyMessage, +) + + +class AccessControlPoliciesResource(DialpadResource): + """AccessControlPoliciesResource resource class + + Handles API operations for: + - /api/v2/accesscontrolpolicies + - /api/v2/accesscontrolpolicies/{id} + - /api/v2/accesscontrolpolicies/{id}/assign + - /api/v2/accesscontrolpolicies/{id}/assignments + - /api/v2/accesscontrolpolicies/{id}/unassign""" + + def assign(self, id: int, request_body: AssignmentPolicyMessage) -> PolicyAssignmentProto: + """Access Control Policies -- Assign + + Assigns a user to an access control policy. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/accesscontrolpolicies/{id}/assign', body=request_body + ) + + def create(self, request_body: CreatePolicyMessage) -> PolicyProto: + """Access Control Policies -- Create + + Creates a new custom access control policy. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/accesscontrolpolicies', body=request_body) + + def delete(self, id: int) -> PolicyProto: + """Access Control Policies -- Delete + + Deletes a policy by marking the state as deleted, and removing all associated users. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/accesscontrolpolicies/{id}') + + def get(self, id: int) -> PolicyProto: + """Access Control Policies -- Get + + Get a specific access control policy's details. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/accesscontrolpolicies/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[PolicyProto]: + """Access Control Policies -- List Policies + + Gets all access control policies belonging to the company. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/accesscontrolpolicies', params={'cursor': cursor} + ) + + def list_assignments( + self, id: int, cursor: Optional[str] = None + ) -> Iterator[PolicyAssignmentProto]: + """Access Control Policies -- List Assignments + + Lists all users assigned to this access control policy. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path=f'/api/v2/accesscontrolpolicies/{id}/assignments', + params={'cursor': cursor}, + ) + + def partial_update(self, id: int, request_body: UpdatePolicyMessage) -> PolicyProto: + """Access Control Policies -- Update + + Updates the provided fields for an existing access control policy. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/accesscontrolpolicies/{id}', body=request_body + ) + + def unassign(self, id: int, request_body: UnassignmentPolicyMessage) -> PolicyAssignmentProto: + """Access Control Policies -- Unassign + + Unassigns one or all target groups associated with the user for an access control policy. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The access control policy's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/accesscontrolpolicies/{id}/unassign', body=request_body + ) diff --git a/src/dialpad/resources/agent_status_event_subscriptions_resource.py b/src/dialpad/resources/agent_status_event_subscriptions_resource.py new file mode 100644 index 0000000..ae59d32 --- /dev/null +++ b/src/dialpad/resources/agent_status_event_subscriptions_resource.py @@ -0,0 +1,119 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.agent_status_event_subscription import ( + AgentStatusEventSubscriptionProto, + CreateAgentStatusEventSubscription, + UpdateAgentStatusEventSubscription, +) + + +class AgentStatusEventSubscriptionsResource(DialpadResource): + """AgentStatusEventSubscriptionsResource resource class + + Handles API operations for: + - /api/v2/subscriptions/agent_status + - /api/v2/subscriptions/agent_status/{id}""" + + def create( + self, request_body: CreateAgentStatusEventSubscription + ) -> AgentStatusEventSubscriptionProto: + """Agent Status -- Create + + Creates an agent status event subscription for your company. A webhook_id is required so that we know to which url the events shall be sent. Please be aware that only call center agent is supported for agent event subscription now. + + See https://developers.dialpad.com/docs/agent-status-events for details on how agent status events work, including the payload structure and payload examples. + + Added on May 7th, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path='/api/v2/subscriptions/agent_status', body=request_body + ) + + def delete(self, id: int) -> AgentStatusEventSubscriptionProto: + """Agent Status -- Delete + + Deletes an agent status event subscription by id. + + Added on May 7th, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/subscriptions/agent_status/{id}') + + def get(self, id: int) -> AgentStatusEventSubscriptionProto: + """Agent Status -- Get + + Gets an agent status event subscription by id. + + Added on May 7th, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/subscriptions/agent_status/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[AgentStatusEventSubscriptionProto]: + """Agent Status -- List + + Gets a list of all the agent status event subscriptions of a company. + + Added on May 7th, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/subscriptions/agent_status', params={'cursor': cursor} + ) + + def partial_update( + self, id: str, request_body: UpdateAgentStatusEventSubscription + ) -> AgentStatusEventSubscriptionProto: + """Agent Status -- Update + + Updates an agent status event subscription by id. + + Added on May 7th, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/subscriptions/agent_status/{id}', body=request_body + ) diff --git a/src/dialpad/resources/app_settings_resource.py b/src/dialpad/resources/app_settings_resource.py new file mode 100644 index 0000000..a026c48 --- /dev/null +++ b/src/dialpad/resources/app_settings_resource.py @@ -0,0 +1,48 @@ +from typing import Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.app.setting import AppSettingProto + + +class AppSettingsResource(DialpadResource): + """AppSettingsResource resource class + + Handles API operations for: + - /api/v2/app/settings""" + + def get( + self, + target_id: Optional[int] = None, + target_type: Optional[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] = None, + ) -> AppSettingProto: + """App Settings -- GET + + Gets the app settings of the OAuth app that is associated with the API key for the target, if target_type and target_id are provided. Otherwise, will return the app settings for the company. + + Rate limit: 1200 per minute. + + Args: + target_id: The target's id. + target_type: The target's type. + + Returns: + A successful response""" + return self._request( + method='GET', + sub_path='/api/v2/app/settings', + params={'target_id': target_id, 'target_type': target_type}, + ) diff --git a/src/dialpad/resources/base.py b/src/dialpad/resources/base.py new file mode 100644 index 0000000..cccb12d --- /dev/null +++ b/src/dialpad/resources/base.py @@ -0,0 +1,30 @@ +from typing import Iterator, Optional + + +class DialpadResource(object): + def __init__(self, client): + self._client = client + + def _request( + self, + method: str = 'GET', + sub_path: Optional[str] = None, + params: Optional[dict] = None, + body: Optional[dict] = None, + headers: Optional[dict] = None, + ) -> dict: + return self._client.request( + method=method, sub_path=sub_path, params=params, body=body, headers=headers + ) + + def _iter_request( + self, + method: str = 'GET', + sub_path: Optional[str] = None, + params: Optional[dict] = None, + body: Optional[dict] = None, + headers: Optional[dict] = None, + ) -> Iterator[dict]: + return self._client.iter_request( + method=method, sub_path=sub_path, params=params, body=body, headers=headers + ) diff --git a/src/dialpad/resources/blocked_numbers_resource.py b/src/dialpad/resources/blocked_numbers_resource.py new file mode 100644 index 0000000..3c2d6fc --- /dev/null +++ b/src/dialpad/resources/blocked_numbers_resource.py @@ -0,0 +1,76 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.blocked_number import ( + AddBlockedNumbersProto, + BlockedNumber, + RemoveBlockedNumbersProto, +) + + +class BlockedNumbersResource(DialpadResource): + """BlockedNumbersResource resource class + + Handles API operations for: + - /api/v2/blockednumbers + - /api/v2/blockednumbers/add + - /api/v2/blockednumbers/remove + - /api/v2/blockednumbers/{number}""" + + def add(self, request_body: AddBlockedNumbersProto) -> None: + """Blocked Number -- Add + + Blocks the specified numbers company-wide. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/blockednumbers/add', body=request_body) + + def get(self, number: str) -> BlockedNumber: + """Blocked Number -- Get + + Gets the specified blocked number. + + Rate limit: 1200 per minute. + + Args: + number: A phone number (e164 format). + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/blockednumbers/{number}') + + def list(self, cursor: Optional[str] = None) -> Iterator[BlockedNumber]: + """Blocked Numbers -- List + + Lists all numbers that have been blocked via the API. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/blockednumbers', params={'cursor': cursor} + ) + + def remove(self, request_body: RemoveBlockedNumbersProto) -> None: + """Blocked Number -- Remove + + Unblocks the specified numbers company-wide. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/blockednumbers/remove', body=request_body) diff --git a/src/dialpad/resources/call_center_operators_resource.py b/src/dialpad/resources/call_center_operators_resource.py new file mode 100644 index 0000000..ac8d295 --- /dev/null +++ b/src/dialpad/resources/call_center_operators_resource.py @@ -0,0 +1,42 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.group import OperatorDutyStatusProto, UpdateOperatorDutyStatusMessage + + +class CallCenterOperatorsResource(DialpadResource): + """CallCenterOperatorsResource resource class + + Handles API operations for: + - /api/v2/callcenters/operators/{id}/dutystatus""" + + def get_duty_status(self, id: int) -> OperatorDutyStatusProto: + """Operator -- Get Duty Status + + Gets the operator's on duty status and reason. + + Rate limit: 1200 per minute. + + Args: + id: The operator's user id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callcenters/operators/{id}/dutystatus') + + def update_duty_status( + self, id: int, request_body: UpdateOperatorDutyStatusMessage + ) -> OperatorDutyStatusProto: + """Operator -- Update Duty Status + + Updates the operator's duty status for all call centers which user belongs to. + + Rate limit: 1200 per minute. + + Args: + id: The operator's user id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/callcenters/operators/{id}/dutystatus', body=request_body + ) diff --git a/src/dialpad/resources/call_centers_resource.py b/src/dialpad/resources/call_centers_resource.py new file mode 100644 index 0000000..55af679 --- /dev/null +++ b/src/dialpad/resources/call_centers_resource.py @@ -0,0 +1,230 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.group import ( + AddCallCenterOperatorMessage, + CallCenterProto, + CallCenterStatusProto, + CreateCallCenterMessage, + OperatorCollection, + OperatorSkillLevelProto, + RemoveCallCenterOperatorMessage, + UpdateCallCenterMessage, + UpdateOperatorSkillLevelMessage, + UserOrRoomProto, +) + + +class CallCentersResource(DialpadResource): + """CallCentersResource resource class + + Handles API operations for: + - /api/v2/callcenters + - /api/v2/callcenters/{call_center_id}/operators/{user_id}/skill + - /api/v2/callcenters/{id} + - /api/v2/callcenters/{id}/operators + - /api/v2/callcenters/{id}/status""" + + def add_operator(self, id: int, request_body: AddCallCenterOperatorMessage) -> UserOrRoomProto: + """Operator -- Add + + Adds an operator to a call center. + + > Warning + > + > This API may result in the usage of call center licenses if required and available. If the licenses are required and not available the operation will fail. Licenses are required when adding an operator that does not have a call center license. + + Added on October 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/callcenters/{id}/operators', body=request_body + ) + + def create(self, request_body: CreateCallCenterMessage) -> CallCenterProto: + """Call Centers -- Create + + Creates a new call center. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/callcenters', body=request_body) + + def delete(self, id: int) -> CallCenterProto: + """Call Centers -- Delete + + Deletes a call center by id. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/callcenters/{id}') + + def get(self, id: int) -> CallCenterProto: + """Call Centers -- Get + + Gets a call center by id. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callcenters/{id}') + + def get_operator_skill_level(self, call_center_id: int, user_id: int) -> OperatorSkillLevelProto: + """Operator -- Get Skill Level + + Gets the skill level of an operator within a call center. + + Rate limit: 1200 per minute. + + Args: + call_center_id: The call center's ID + user_id: The operator's ID + + Returns: + A successful response""" + return self._request( + method='GET', sub_path=f'/api/v2/callcenters/{call_center_id}/operators/{user_id}/skill' + ) + + def get_status(self, id: int) -> CallCenterStatusProto: + """Call Centers -- Status + + Gets live status information on the corresponding Call Center. + + Added on August 7, 2023 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callcenters/{id}/status') + + def list( + self, + cursor: Optional[str] = None, + name_search: Optional[str] = None, + office_id: Optional[int] = None, + ) -> Iterator[CallCenterProto]: + """Call Centers -- List + + Gets all the call centers for the company. Added on Feb 3, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + name_search: search call centers by name or search by the substring of the name. If input example is 'Cool', output example can be a list of call centers whose name contains the string + 'Cool' - ['Cool call center 1', 'Cool call center 2049'] + office_id: search call center by office. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/callcenters', + params={'cursor': cursor, 'office_id': office_id, 'name_search': name_search}, + ) + + def list_operators(self, id: int) -> OperatorCollection: + """Operators -- List + + Gets operators for a call center. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callcenters/{id}/operators') + + def partial_update(self, id: int, request_body: UpdateCallCenterMessage) -> CallCenterProto: + """Call Centers -- Update + + Updates a call center by id. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/callcenters/{id}', body=request_body) + + def remove_operator( + self, id: int, request_body: RemoveCallCenterOperatorMessage + ) -> UserOrRoomProto: + """Operator -- Remove + + Removes an operator from a call center. + + Note: This API will not change or release any licenses. + + Added on October 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='DELETE', sub_path=f'/api/v2/callcenters/{id}/operators', body=request_body + ) + + def update_operator_skill_level( + self, call_center_id: int, user_id: int, request_body: UpdateOperatorSkillLevelMessage + ) -> OperatorSkillLevelProto: + """Operator -- Update Skill Level + + Updates the skill level of an operator within a call center. + + Rate limit: 1200 per minute. + + Args: + call_center_id: The call center's ID + user_id: The operator's ID + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', + sub_path=f'/api/v2/callcenters/{call_center_id}/operators/{user_id}/skill', + body=request_body, + ) diff --git a/src/dialpad/resources/call_event_subscriptions_resource.py b/src/dialpad/resources/call_event_subscriptions_resource.py new file mode 100644 index 0000000..223c198 --- /dev/null +++ b/src/dialpad/resources/call_event_subscriptions_resource.py @@ -0,0 +1,140 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call_event_subscription import ( + CallEventSubscriptionProto, + CreateCallEventSubscription, + UpdateCallEventSubscription, +) + + +class CallEventSubscriptionsResource(DialpadResource): + """CallEventSubscriptionsResource resource class + + Handles API operations for: + - /api/v2/subscriptions/call + - /api/v2/subscriptions/call/{id}""" + + def create(self, request_body: CreateCallEventSubscription) -> CallEventSubscriptionProto: + """Call Event -- Create + + Creates a call event subscription. A webhook_id is required so that we know to which url the events shall be sent. Call states can be used to limit the states for which call events are sent. A target_type and target_id may optionally be provided to scope the events only to the calls to/from that target. + + See https://developers.dialpad.com/docs/call-events-logging for details on how call events work, + including the payload structure, the meaning of different call states, and payload examples. + + Note: **To include the recording url in call events, your API key needs to have the + "recordings_export" OAuth scope. For Dialpad Meetings call events, your API key needs to have the "conference:all" OAuth scope.** + + Added on April 23rd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/subscriptions/call', body=request_body) + + def delete(self, id: int) -> CallEventSubscriptionProto: + """Call Event -- Delete + + Deletes a call event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/subscriptions/call/{id}') + + def get(self, id: int) -> CallEventSubscriptionProto: + """Call Event -- Get + + Gets a call event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/subscriptions/call/{id}') + + def list( + self, + cursor: Optional[str] = None, + target_id: Optional[int] = None, + target_type: Optional[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] = None, + ) -> Iterator[CallEventSubscriptionProto]: + """Call Event -- List + + Gets a list of all the call event subscriptions of a company or of a target. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + target_id: The target's id. + target_type: Target's type. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/subscriptions/call', + params={'cursor': cursor, 'target_type': target_type, 'target_id': target_id}, + ) + + def partial_update( + self, id: int, request_body: UpdateCallEventSubscription + ) -> CallEventSubscriptionProto: + """Call Event -- Update + + Updates a call event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/subscriptions/call/{id}', body=request_body + ) diff --git a/src/dialpad/resources/call_labels_resource.py b/src/dialpad/resources/call_labels_resource.py new file mode 100644 index 0000000..aa94650 --- /dev/null +++ b/src/dialpad/resources/call_labels_resource.py @@ -0,0 +1,27 @@ +from typing import Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call_label import CompanyCallLabels + + +class CallLabelsResource(DialpadResource): + """CallLabelsResource resource class + + Handles API operations for: + - /api/v2/calllabels""" + + def list(self, limit: Optional[int] = None) -> CompanyCallLabels: + """Label -- List + + Gets all labels for a determined company. + + Added on Nov 15, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + limit: The maximum number of results to return. + + Returns: + A successful response""" + return self._request(method='GET', sub_path='/api/v2/calllabels', params={'limit': limit}) diff --git a/src/dialpad/resources/call_review_share_links_resource.py b/src/dialpad/resources/call_review_share_links_resource.py new file mode 100644 index 0000000..3a22cb2 --- /dev/null +++ b/src/dialpad/resources/call_review_share_links_resource.py @@ -0,0 +1,81 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call_review_share_link import ( + CallReviewShareLink, + CreateCallReviewShareLink, + UpdateCallReviewShareLink, +) + + +class CallReviewShareLinksResource(DialpadResource): + """CallReviewShareLinksResource resource class + + Handles API operations for: + - /api/v2/callreviewsharelink + - /api/v2/callreviewsharelink/{id}""" + + def create(self, request_body: CreateCallReviewShareLink) -> CallReviewShareLink: + """Call Review Sharelink -- Create + + Create a call review share link by call id. + + Added on Sep 21, 2022 for API v2. + + Rate limit: 250 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/callreviewsharelink', body=request_body) + + def delete(self, id: str) -> CallReviewShareLink: + """Call Review Sharelink -- Delete + + Delete a call review share link by id. + + Added on Sep 21, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The share link's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/callreviewsharelink/{id}') + + def get(self, id: str) -> CallReviewShareLink: + """Call Review Sharelink -- Get + + Gets a call review share link by call id. + + Added on Sep 21, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The share link's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callreviewsharelink/{id}') + + def update(self, id: str, request_body: UpdateCallReviewShareLink) -> CallReviewShareLink: + """Call Review Sharelink -- Update + + Update a call review share link by id. + + Added on Sep 21, 2022 for API v2. + + Rate limit: 250 per minute. + + Args: + id: The share link's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PUT', sub_path=f'/api/v2/callreviewsharelink/{id}', body=request_body + ) diff --git a/src/dialpad/resources/call_routers_resource.py b/src/dialpad/resources/call_routers_resource.py new file mode 100644 index 0000000..eaf389c --- /dev/null +++ b/src/dialpad/resources/call_routers_resource.py @@ -0,0 +1,113 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call_router import ( + ApiCallRouterProto, + CreateApiCallRouterMessage, + UpdateApiCallRouterMessage, +) +from dialpad.schemas.number import AssignNumberMessage, NumberProto + + +class CallRoutersResource(DialpadResource): + """CallRoutersResource resource class + + Handles API operations for: + - /api/v2/callrouters + - /api/v2/callrouters/{id} + - /api/v2/callrouters/{id}/assign_number""" + + def assign_number(self, id: int, request_body: AssignNumberMessage) -> NumberProto: + """Dialpad Number -- Assign + + Assigns a number to a callrouter. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. + + Rate limit: 1200 per minute. + + Args: + id: The API call router's ID + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/callrouters/{id}/assign_number', body=request_body + ) + + def create(self, request_body: CreateApiCallRouterMessage) -> ApiCallRouterProto: + """Call Router -- Create + + Creates a new API-based call router. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/callrouters', body=request_body) + + def delete(self, id: str) -> None: + """Call Router -- Delete + + Deletes the API call router with the given ID. + + Rate limit: 1200 per minute. + + Args: + id: The API call router's ID + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/callrouters/{id}') + + def get(self, id: int) -> ApiCallRouterProto: + """Call Router -- Get + + Gets the API call router with the given ID. + + Rate limit: 1200 per minute. + + Args: + id: The API call router's ID + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/callrouters/{id}') + + def list( + self, cursor: Optional[str] = None, office_id: Optional[int] = None + ) -> Iterator[ApiCallRouterProto]: + """Call Router -- List + + Lists all of the API call routers for a given company or office. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + office_id: The office's id. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/callrouters', + params={'cursor': cursor, 'office_id': office_id}, + ) + + def partial_update(self, id: str, request_body: UpdateApiCallRouterMessage) -> ApiCallRouterProto: + """Call Router -- Update + + Updates the API call router with the given ID. + + Rate limit: 1 per 5 minute. + + Args: + id: The API call router's ID + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/callrouters/{id}', body=request_body) diff --git a/src/dialpad/resources/callbacks_resource.py b/src/dialpad/resources/callbacks_resource.py new file mode 100644 index 0000000..c9bfc5f --- /dev/null +++ b/src/dialpad/resources/callbacks_resource.py @@ -0,0 +1,43 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call import CallbackMessage, CallbackProto, ValidateCallbackProto + + +class CallbacksResource(DialpadResource): + """CallbacksResource resource class + + Handles API operations for: + - /api/v2/callback + - /api/v2/callback/validate""" + + def enqueue_callback(self, request_body: CallbackMessage) -> CallbackProto: + """Call Back -- Enqueue + + Requests a call back to a given number by an operator in a given call center. The call back is added to the queue for the call center like a regular call, and a call is initiated when the next operator becomes available. This API respects all existing call center settings, + e.g. business / holiday hours and queue settings. This API currently does not allow international call backs. Duplicate call backs for a given number and call center are not allowed. Specific error messages will be provided in case of failure. + + Added on Dec 9, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/callback', body=request_body) + + def validate_callback(self, request_body: CallbackMessage) -> ValidateCallbackProto: + """Call Back -- Validate + + Performs a dry-run of creating a callback request, without adding it to the call center queue. + + This performs the same validation logic as when actually enqueuing a callback request, allowing early identification of problems which would prevent a successful callback request. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/callback/validate', body=request_body) diff --git a/src/dialpad/resources/calls_resource.py b/src/dialpad/resources/calls_resource.py new file mode 100644 index 0000000..415deff --- /dev/null +++ b/src/dialpad/resources/calls_resource.py @@ -0,0 +1,216 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call import ( + AddCallLabelsMessage, + AddParticipantMessage, + CallProto, + InitiatedIVRCallProto, + OutboundIVRMessage, + RingCallMessage, + RingCallProto, + TransferCallMessage, + TransferredCallProto, + UnparkCallMessage, +) + + +class CallsResource(DialpadResource): + """CallsResource resource class + + Handles API operations for: + - /api/v2/call + - /api/v2/call/initiate_ivr_call + - /api/v2/call/{id} + - /api/v2/call/{id}/actions/hangup + - /api/v2/call/{id}/labels + - /api/v2/call/{id}/participants/add + - /api/v2/call/{id}/transfer + - /api/v2/call/{id}/unpark""" + + def add_participant(self, id: int, request_body: AddParticipantMessage) -> RingCallProto: + """Call -- Add Participant + + Adds another participant to a call. Valid methods to add are by phone or by target. Targets require to have a primary phone Added on Nov 11, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/call/{id}/participants/add', body=request_body + ) + + def get(self, id: int) -> CallProto: + """Call -- Get + + Get Call status and other information. Added on May 25, 2021 for API v2. + + Rate limit: 10 per minute. + + Args: + id: The call's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/call/{id}') + + def hangup_call(self, id: int) -> None: + """Call Actions -- Hang up + + Hangs up the call. Added on Oct 25, 2024 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call's id. + + Returns: + A successful response""" + return self._request(method='PUT', sub_path=f'/api/v2/call/{id}/actions/hangup') + + def initiate_ivr_call(self, request_body: OutboundIVRMessage) -> InitiatedIVRCallProto: + """Call -- Initiate IVR Call + + Initiates an outbound call to ring an IVR Workflow. + + Added on Aug 14, 2023 for API v2. + + Rate limit: 10 per minute per IVR. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path='/api/v2/call/initiate_ivr_call', body=request_body + ) + + def initiate_ring_call(self, request_body: RingCallMessage) -> RingCallProto: + """Call -- Initiate via Ring + + Initiates an outbound call to ring all devices (or a single specified device). + + Added on Feb 20, 2020 for API v2. + + Rate limit: 5 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/call', body=request_body) + + def list( + self, + cursor: Optional[str] = None, + started_after: Optional[int] = None, + started_before: Optional[int] = None, + target_id: Optional[int] = None, + target_type: Optional[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] = None, + ) -> Iterator[CallProto]: + """Call -- List + + Provides a paginated list of calls matching the specified filter parameters in reverse-chronological order by call start time (i.e. recent calls first) + + Note: This API will only include calls that have already concluded. + + Added on May 27, 2024 for API v2. + + Requires a company admin API key. + + Requires scope: ``calls:list`` + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + started_after: Only includes calls that started more recently than the specified timestamp. + (UTC ms-since-epoch timestamp) + started_before: Only includes calls that started prior to the specified timestamp. + (UTC ms-since-epoch timestamp) + target_id: The ID of a target to filter against. + target_type: The target type associated with the target ID. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/call', + params={ + 'cursor': cursor, + 'started_after': started_after, + 'started_before': started_before, + 'target_id': target_id, + 'target_type': target_type, + }, + ) + + def set_call_label(self, id: int, request_body: AddCallLabelsMessage) -> CallProto: + """Label -- Set + + Set Labels for a determined call id. + + Added on Nov 15, 2022 for API v2. + + Rate limit: 250 per minute. + + Args: + id: The call's id + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PUT', sub_path=f'/api/v2/call/{id}/labels', body=request_body) + + def transfer(self, id: int, request_body: TransferCallMessage) -> TransferredCallProto: + """Call -- Transfer + + Transfers call to another recipient. Added on Sep 25, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path=f'/api/v2/call/{id}/transfer', body=request_body) + + def unpark(self, id: int, request_body: UnparkCallMessage) -> RingCallProto: + """Call -- Unpark + + Unparks call from Office mainline. Added on Nov 11, 2024 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The call's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path=f'/api/v2/call/{id}/unpark', body=request_body) diff --git a/src/dialpad/resources/changelog_event_subscriptions_resource.py b/src/dialpad/resources/changelog_event_subscriptions_resource.py new file mode 100644 index 0000000..ad0a4a9 --- /dev/null +++ b/src/dialpad/resources/changelog_event_subscriptions_resource.py @@ -0,0 +1,129 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.change_log_event_subscription import ( + ChangeLogEventSubscriptionProto, + CreateChangeLogEventSubscription, + UpdateChangeLogEventSubscription, +) + + +class ChangelogEventSubscriptionsResource(DialpadResource): + """ChangelogEventSubscriptionsResource resource class + + Handles API operations for: + - /api/v2/subscriptions/changelog + - /api/v2/subscriptions/changelog/{id}""" + + def create( + self, request_body: CreateChangeLogEventSubscription + ) -> ChangeLogEventSubscriptionProto: + """Change Log -- Create + + Creates a change log event subscription for your company. An endpoint_id is required so that we know to which url the events shall be sent. + + See https://developers.dialpad.com/docs/change-log-events for details on how change log events work, including the payload structure and payload examples. + + Added on December 9th, 2022 for API v2. + + Requires a company admin API key. + + Requires scope: ``change_log`` + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path='/api/v2/subscriptions/changelog', body=request_body + ) + + def delete(self, id: int) -> ChangeLogEventSubscriptionProto: + """Change Log -- Delete + + Deletes a change log event subscription by id. + + Added on December 9th, 2022 for API v2. + + Requires a company admin API key. + + Requires scope: ``change_log`` + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/subscriptions/changelog/{id}') + + def get(self, id: int) -> ChangeLogEventSubscriptionProto: + """Change Log -- Get + + Gets a change log event subscription by id. + + Added on December 9th, 2022 for API v2. + + Requires a company admin API key. + + Requires scope: ``change_log`` + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/subscriptions/changelog/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[ChangeLogEventSubscriptionProto]: + """Change Log -- List + + Gets a list of all the change log event subscriptions of a company. + + Added on December 9th, 2022 for API v2. + + Requires a company admin API key. + + Requires scope: ``change_log`` + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/subscriptions/changelog', params={'cursor': cursor} + ) + + def partial_update( + self, id: str, request_body: UpdateChangeLogEventSubscription + ) -> ChangeLogEventSubscriptionProto: + """Change Log -- Update + + Updates change log event subscription by id. + + Added on December 9th, 2022 for API v2. + + Requires a company admin API key. + + Requires scope: ``change_log`` + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/subscriptions/changelog/{id}', body=request_body + ) diff --git a/src/dialpad/resources/channels_resource.py b/src/dialpad/resources/channels_resource.py new file mode 100644 index 0000000..035eb0a --- /dev/null +++ b/src/dialpad/resources/channels_resource.py @@ -0,0 +1,144 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.channel import ChannelProto, CreateChannelMessage +from dialpad.schemas.member_channel import ( + AddChannelMemberMessage, + MembersProto, + RemoveChannelMemberMessage, +) + + +class ChannelsResource(DialpadResource): + """ChannelsResource resource class + + Handles API operations for: + - /api/v2/channels + - /api/v2/channels/{id} + - /api/v2/channels/{id}/members""" + + def add_member(self, id: int, request_body: AddChannelMemberMessage) -> MembersProto: + """Member -- Add + + Adds an user to a channel. + + Added on May 12, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The channel's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/channels/{id}/members', body=request_body + ) + + def create(self, request_body: CreateChannelMessage) -> ChannelProto: + """Channel -- Create + + Creates a new channel. + + Added on May 11, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/channels', body=request_body) + + def delete(self, id: int) -> None: + """Channel -- Delete + + Deletes a channel by id. + + Added on May 11, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The channel id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/channels/{id}') + + def get(self, id: int) -> ChannelProto: + """Channel -- Get + + Get channel by id + + Added on May 11, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The channel id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/channels/{id}') + + def list( + self, cursor: Optional[str] = None, state: Optional[str] = None + ) -> Iterator[ChannelProto]: + """Channel -- List + + Lists all channels in the company. + + Added on May 11, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + state: The state of the channel. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/channels', params={'cursor': cursor, 'state': state} + ) + + def list_members(self, id: int, cursor: Optional[str] = None) -> Iterator[MembersProto]: + """Members -- List + + List all the members from a channel + + Added on May 11, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The channel id + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path=f'/api/v2/channels/{id}/members', params={'cursor': cursor} + ) + + def remove_member(self, id: int, request_body: RemoveChannelMemberMessage) -> None: + """Member -- Remove + + Removes a member from a channel. + + Added on May 12, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The channel's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='DELETE', sub_path=f'/api/v2/channels/{id}/members', body=request_body + ) diff --git a/src/dialpad/resources/coaching_teams_resource.py b/src/dialpad/resources/coaching_teams_resource.py new file mode 100644 index 0000000..0d7ce06 --- /dev/null +++ b/src/dialpad/resources/coaching_teams_resource.py @@ -0,0 +1,80 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.coaching_team import ( + CoachingTeamMemberMessage, + CoachingTeamMemberProto, + CoachingTeamProto, +) + + +class CoachingTeamsResource(DialpadResource): + """CoachingTeamsResource resource class + + Handles API operations for: + - /api/v2/coachingteams + - /api/v2/coachingteams/{id} + - /api/v2/coachingteams/{id}/members""" + + def add_member(self, id: int, request_body: CoachingTeamMemberMessage) -> CoachingTeamMemberProto: + """Coaching Team -- Add Member + + Add a user to the specified coaching team as trainee or coach. + + Added on July 5th, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: Id of the coaching team + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/coachingteams/{id}/members', body=request_body + ) + + def get(self, id: int) -> CoachingTeamProto: + """Coaching Team -- Get + + Get details of a specified coaching team. Added on Jul 30th, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: Id of the coaching team + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/coachingteams/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[CoachingTeamProto]: + """Coaching Team -- List + + Get a list of all coaching teams in the company. Added on Feb 3rd, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/coachingteams', params={'cursor': cursor} + ) + + def list_members(self, id: int) -> Iterator[CoachingTeamMemberProto]: + """Coaching Team -- List Members + + Get a list of members of a coaching team. Added on Jul 30th, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: Id of the coaching team + + Returns: + An iterator of items from A successful response""" + return self._iter_request(method='GET', sub_path=f'/api/v2/coachingteams/{id}/members') diff --git a/src/dialpad/resources/company_resource.py b/src/dialpad/resources/company_resource.py new file mode 100644 index 0000000..973c4a6 --- /dev/null +++ b/src/dialpad/resources/company_resource.py @@ -0,0 +1,58 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.company import CompanyProto +from dialpad.schemas.sms_opt_out import SmsOptOutEntryProto + + +class CompanyResource(DialpadResource): + """CompanyResource resource class + + Handles API operations for: + - /api/v2/company + - /api/v2/company/{id}/smsoptout""" + + def get(self) -> CompanyProto: + """Company -- Get + + Gets company information. + + Added on Feb 21, 2019 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Returns: + A successful response""" + return self._request(method='GET', sub_path='/api/v2/company') + + def get_sms_opt_out_list( + self, + id: str, + opt_out_state: Literal['opted_back_in', 'opted_out'], + a2p_campaign_id: Optional[int] = None, + cursor: Optional[str] = None, + ) -> Iterator[SmsOptOutEntryProto]: + """Company -- Get SMS Opt-out List + + + + Requires a company admin API key. + + Rate limit: 250 per minute. + + Args: + id: ID of the requested company. This is required and must be specified in the URL path. The value must match the id from the company linked to the API key. + a2p_campaign_id: Optional company A2P campaign entity id to filter results by. Note, if set, + then the parameter 'opt_out_state' must be also set to the value 'opted_out'. + cursor: Optional token used to return the next page of a previous request. Use the cursor provided in the previous response. + opt_out_state: Required opt-out state to filter results by. Only results matching this state will be returned. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path=f'/api/v2/company/{id}/smsoptout', + params={'a2p_campaign_id': a2p_campaign_id, 'cursor': cursor, 'opt_out_state': opt_out_state}, + ) diff --git a/src/dialpad/resources/contact_event_subscriptions_resource.py b/src/dialpad/resources/contact_event_subscriptions_resource.py new file mode 100644 index 0000000..3526e3a --- /dev/null +++ b/src/dialpad/resources/contact_event_subscriptions_resource.py @@ -0,0 +1,123 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.contact_event_subscription import ( + ContactEventSubscriptionProto, + CreateContactEventSubscription, + UpdateContactEventSubscription, +) + + +class ContactEventSubscriptionsResource(DialpadResource): + """ContactEventSubscriptionsResource resource class + + Handles API operations for: + - /api/v2/subscriptions/contact + - /api/v2/subscriptions/contact/{id}""" + + def create(self, request_body: CreateContactEventSubscription) -> ContactEventSubscriptionProto: + """Contact Event -- Create + + Creates a contact event subscription for your company. A webhook_id is required so that we know to which url the events shall be sent. + + See https://developers.dialpad.com/docs/contact-events for details on how contact events work, including the payload structure and payload examples. + + Added on April 23rd, 2021 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/subscriptions/contact', body=request_body) + + def delete(self, id: int) -> ContactEventSubscriptionProto: + """Contact Event -- Delete + + Deletes a contact event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/subscriptions/contact/{id}') + + def get(self, id: int) -> ContactEventSubscriptionProto: + """Contact Event -- Get + + Gets a contact event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/subscriptions/contact/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[ContactEventSubscriptionProto]: + """Contact Event -- List + + Gets a list of all the contact event subscriptions of a company. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/subscriptions/contact', params={'cursor': cursor} + ) + + def partial_update( + self, id: int, request_body: UpdateContactEventSubscription + ) -> ContactEventSubscriptionProto: + """Contact Event -- Update + + Updates a contact event subscription by id. + + Added on April 23rd, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/subscriptions/contact/{id}', body=request_body + ) diff --git a/src/dialpad/resources/contacts_resource.py b/src/dialpad/resources/contacts_resource.py new file mode 100644 index 0000000..b7565ec --- /dev/null +++ b/src/dialpad/resources/contacts_resource.py @@ -0,0 +1,117 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.contact import ( + ContactProto, + CreateContactMessage, + CreateContactMessageWithUid, + UpdateContactMessage, +) + + +class ContactsResource(DialpadResource): + """ContactsResource resource class + + Handles API operations for: + - /api/v2/contacts + - /api/v2/contacts/{id}""" + + def create(self, request_body: CreateContactMessage) -> ContactProto: + """Contact -- Create + + Creates a new contact. Added on Mar 2, 2020 for API v2. + + Rate limit: 100 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/contacts', body=request_body) + + def create_or_update(self, request_body: CreateContactMessageWithUid) -> ContactProto: + """Contact -- Create or Update + + Creates a new shared contact with uid. Added on Jun 11, 2020 for API v2. + + Rate limit: 100 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PUT', sub_path='/api/v2/contacts', body=request_body) + + def delete(self, id: str) -> ContactProto: + """Contact -- Delete + + Deletes a contact by id. Added on Mar 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The contact's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/contacts/{id}') + + def get(self, id: str) -> ContactProto: + """Contact -- Get + + Gets a contact by id. Currently, only contacts of type shared and local can be retrieved by this API. + + Added on Mar 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The contact's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/contacts/{id}') + + def list( + self, + cursor: Optional[str] = None, + include_local: Optional[bool] = None, + owner_id: Optional[str] = None, + ) -> Iterator[ContactProto]: + """Contact -- List + + Gets company shared contacts, or user's local contacts if owner_id is provided. + + Added on Mar 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + include_local: If set to True company local contacts will be included. default False. + owner_id: The id of the user who owns the contact. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/contacts', + params={'cursor': cursor, 'include_local': include_local, 'owner_id': owner_id}, + ) + + def partial_update(self, id: str, request_body: UpdateContactMessage) -> ContactProto: + """Contact -- Update + + Updates the provided fields for an existing contact. Added on Mar 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The contact's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/contacts/{id}', body=request_body) diff --git a/src/dialpad/resources/custom_ivrs_resource.py b/src/dialpad/resources/custom_ivrs_resource.py new file mode 100644 index 0000000..9a8995f --- /dev/null +++ b/src/dialpad/resources/custom_ivrs_resource.py @@ -0,0 +1,307 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.custom_ivr import ( + CreateCustomIvrMessage, + CustomIvrDetailsProto, + CustomIvrProto, + UpdateCustomIvrDetailsMessage, + UpdateCustomIvrMessage, +) + + +class CustomIVRsResource(DialpadResource): + """CustomIVRsResource resource class + + Handles API operations for: + - /api/v2/customivrs + - /api/v2/customivrs/{ivr_id} + - /api/v2/customivrs/{target_type}/{target_id}/{ivr_type}""" + + def assign( + self, + ivr_type: Literal[ + 'ASK_FIRST_OPERATOR_NOT_AVAILABLE', + 'AUTO_RECORDING', + 'CALLAI_AUTO_RECORDING', + 'CG_AUTO_RECORDING', + 'CLOSED', + 'CLOSED_DEPARTMENT_INTRO', + 'CLOSED_MENU', + 'CLOSED_MENU_OPTION', + 'CSAT_INTRO', + 'CSAT_OUTRO', + 'CSAT_PREAMBLE', + 'CSAT_QUESTION', + 'DEPARTMENT_INTRO', + 'GREETING', + 'HOLD_AGENT_READY', + 'HOLD_APPREC', + 'HOLD_CALLBACK_ACCEPT', + 'HOLD_CALLBACK_ACCEPTED', + 'HOLD_CALLBACK_CONFIRM', + 'HOLD_CALLBACK_CONFIRM_NUMBER', + 'HOLD_CALLBACK_DIFFERENT_NUMBER', + 'HOLD_CALLBACK_DIRECT', + 'HOLD_CALLBACK_FULFILLED', + 'HOLD_CALLBACK_INVALID_NUMBER', + 'HOLD_CALLBACK_KEYPAD', + 'HOLD_CALLBACK_REJECT', + 'HOLD_CALLBACK_REJECTED', + 'HOLD_CALLBACK_REQUEST', + 'HOLD_CALLBACK_REQUESTED', + 'HOLD_CALLBACK_SAME_NUMBER', + 'HOLD_CALLBACK_TRY_AGAIN', + 'HOLD_CALLBACK_UNDIALABLE', + 'HOLD_ESCAPE_VM_EIGHT', + 'HOLD_ESCAPE_VM_FIVE', + 'HOLD_ESCAPE_VM_FOUR', + 'HOLD_ESCAPE_VM_NINE', + 'HOLD_ESCAPE_VM_ONE', + 'HOLD_ESCAPE_VM_POUND', + 'HOLD_ESCAPE_VM_SEVEN', + 'HOLD_ESCAPE_VM_SIX', + 'HOLD_ESCAPE_VM_STAR', + 'HOLD_ESCAPE_VM_TEN', + 'HOLD_ESCAPE_VM_THREE', + 'HOLD_ESCAPE_VM_TWO', + 'HOLD_ESCAPE_VM_ZERO', + 'HOLD_INTERRUPT', + 'HOLD_INTRO', + 'HOLD_MUSIC', + 'HOLD_POSITION_EIGHT', + 'HOLD_POSITION_FIVE', + 'HOLD_POSITION_FOUR', + 'HOLD_POSITION_MORE', + 'HOLD_POSITION_NINE', + 'HOLD_POSITION_ONE', + 'HOLD_POSITION_SEVEN', + 'HOLD_POSITION_SIX', + 'HOLD_POSITION_TEN', + 'HOLD_POSITION_THREE', + 'HOLD_POSITION_TWO', + 'HOLD_POSITION_ZERO', + 'HOLD_WAIT', + 'MENU', + 'MENU_OPTION', + 'NEXT_TARGET', + 'VM_DROP_MESSAGE', + 'VM_UNAVAILABLE', + 'VM_UNAVAILABLE_CLOSED', + ], + target_id: int, + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ], + request_body: UpdateCustomIvrMessage, + ) -> CustomIvrProto: + """Custom IVR -- Assign + + Sets an existing Ivr for a target. + + Added on July 27, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + ivr_type: Type of ivr you want to update + target_id: The target's id. + target_type: Target's type. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', + sub_path=f'/api/v2/customivrs/{target_type}/{target_id}/{ivr_type}', + body=request_body, + ) + + def create(self, request_body: CreateCustomIvrMessage) -> CustomIvrDetailsProto: + """Custom IVR -- Create + + Creates a new custom IVR for a target. + + Added on June 15, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/customivrs', body=request_body) + + def list( + self, + target_id: int, + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ], + cursor: Optional[str] = None, + ) -> Iterator[CustomIvrProto]: + """Custom IVR -- Get + + Gets all the custom IVRs for a target. + + Added on July 14, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + target_id: The target's id. + target_type: Target's type. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/customivrs', + params={'cursor': cursor, 'target_type': target_type, 'target_id': target_id}, + ) + + def partial_update( + self, ivr_id: str, request_body: UpdateCustomIvrDetailsMessage + ) -> CustomIvrDetailsProto: + """Custom IVR -- Update + + Update the name or description of an existing custom ivr. + + Rate limit: 1200 per minute. + + Args: + ivr_id: The ID of the custom ivr to be updated. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/customivrs/{ivr_id}', body=request_body) + + def unassign( + self, + ivr_type: Literal[ + 'ASK_FIRST_OPERATOR_NOT_AVAILABLE', + 'AUTO_RECORDING', + 'CALLAI_AUTO_RECORDING', + 'CG_AUTO_RECORDING', + 'CLOSED', + 'CLOSED_DEPARTMENT_INTRO', + 'CLOSED_MENU', + 'CLOSED_MENU_OPTION', + 'CSAT_INTRO', + 'CSAT_OUTRO', + 'CSAT_PREAMBLE', + 'CSAT_QUESTION', + 'DEPARTMENT_INTRO', + 'GREETING', + 'HOLD_AGENT_READY', + 'HOLD_APPREC', + 'HOLD_CALLBACK_ACCEPT', + 'HOLD_CALLBACK_ACCEPTED', + 'HOLD_CALLBACK_CONFIRM', + 'HOLD_CALLBACK_CONFIRM_NUMBER', + 'HOLD_CALLBACK_DIFFERENT_NUMBER', + 'HOLD_CALLBACK_DIRECT', + 'HOLD_CALLBACK_FULFILLED', + 'HOLD_CALLBACK_INVALID_NUMBER', + 'HOLD_CALLBACK_KEYPAD', + 'HOLD_CALLBACK_REJECT', + 'HOLD_CALLBACK_REJECTED', + 'HOLD_CALLBACK_REQUEST', + 'HOLD_CALLBACK_REQUESTED', + 'HOLD_CALLBACK_SAME_NUMBER', + 'HOLD_CALLBACK_TRY_AGAIN', + 'HOLD_CALLBACK_UNDIALABLE', + 'HOLD_ESCAPE_VM_EIGHT', + 'HOLD_ESCAPE_VM_FIVE', + 'HOLD_ESCAPE_VM_FOUR', + 'HOLD_ESCAPE_VM_NINE', + 'HOLD_ESCAPE_VM_ONE', + 'HOLD_ESCAPE_VM_POUND', + 'HOLD_ESCAPE_VM_SEVEN', + 'HOLD_ESCAPE_VM_SIX', + 'HOLD_ESCAPE_VM_STAR', + 'HOLD_ESCAPE_VM_TEN', + 'HOLD_ESCAPE_VM_THREE', + 'HOLD_ESCAPE_VM_TWO', + 'HOLD_ESCAPE_VM_ZERO', + 'HOLD_INTERRUPT', + 'HOLD_INTRO', + 'HOLD_MUSIC', + 'HOLD_POSITION_EIGHT', + 'HOLD_POSITION_FIVE', + 'HOLD_POSITION_FOUR', + 'HOLD_POSITION_MORE', + 'HOLD_POSITION_NINE', + 'HOLD_POSITION_ONE', + 'HOLD_POSITION_SEVEN', + 'HOLD_POSITION_SIX', + 'HOLD_POSITION_TEN', + 'HOLD_POSITION_THREE', + 'HOLD_POSITION_TWO', + 'HOLD_POSITION_ZERO', + 'HOLD_WAIT', + 'MENU', + 'MENU_OPTION', + 'NEXT_TARGET', + 'VM_DROP_MESSAGE', + 'VM_UNAVAILABLE', + 'VM_UNAVAILABLE_CLOSED', + ], + target_id: int, + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ], + request_body: UpdateCustomIvrMessage, + ) -> CustomIvrDetailsProto: + """Custom IVR -- Delete + + Delete and un-assign an Ivr from a target. + + Rate limit: 1200 per minute. + + Args: + ivr_type: Type of ivr you want to update. + target_id: The id of the target. + target_type: Target's type. of the custom ivr to be updated. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='DELETE', + sub_path=f'/api/v2/customivrs/{target_type}/{target_id}/{ivr_type}', + body=request_body, + ) diff --git a/src/dialpad/resources/departments_resource.py b/src/dialpad/resources/departments_resource.py new file mode 100644 index 0000000..c781834 --- /dev/null +++ b/src/dialpad/resources/departments_resource.py @@ -0,0 +1,163 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.group import ( + AddOperatorMessage, + CreateDepartmentMessage, + DepartmentProto, + OperatorCollection, + RemoveOperatorMessage, + UpdateDepartmentMessage, + UserOrRoomProto, +) + + +class DepartmentsResource(DialpadResource): + """DepartmentsResource resource class + + Handles API operations for: + - /api/v2/departments + - /api/v2/departments/{id} + - /api/v2/departments/{id}/operators""" + + def add_operator(self, id: int, request_body: AddOperatorMessage) -> UserOrRoomProto: + """Operator -- Add + + Adds an operator to a department. + + Added on October 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The department's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/departments/{id}/operators', body=request_body + ) + + def create(self, request_body: CreateDepartmentMessage) -> DepartmentProto: + """Departments-- Create + + Creates a new department. + + Added on March 25th, 2022 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/departments', body=request_body) + + def delete(self, id: int) -> DepartmentProto: + """Departments-- Delete + + Deletes a department by id. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The department's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/departments/{id}') + + def get(self, id: int) -> DepartmentProto: + """Department -- Get + + Gets a department by id. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The department's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/departments/{id}') + + def list( + self, + cursor: Optional[str] = None, + name_search: Optional[str] = None, + office_id: Optional[int] = None, + ) -> Iterator[DepartmentProto]: + """Department -- List + + Gets all the departments in the company. Added on Feb 3rd, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + name_search: search departments by name or search by the substring of the name. If input example is 'Happy', output example can be a list of departments whose name contains the string Happy - ['Happy department 1', 'Happy department 2'] + office_id: filter departments by office. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/departments', + params={'cursor': cursor, 'office_id': office_id, 'name_search': name_search}, + ) + + def list_operators(self, id: int) -> OperatorCollection: + """Operator -- List + + Gets operators for a department. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The department's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/departments/{id}/operators') + + def partial_update(self, id: int, request_body: UpdateDepartmentMessage) -> DepartmentProto: + """Departments-- Update + + Updates a new department. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + id: The call center's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/departments/{id}', body=request_body) + + def remove_operator(self, id: int, request_body: RemoveOperatorMessage) -> UserOrRoomProto: + """Operator -- Remove + + Removes an operator from a department. + + Added on October 2, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The department's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='DELETE', sub_path=f'/api/v2/departments/{id}/operators', body=request_body + ) diff --git a/src/dialpad/resources/fax_lines_resource.py b/src/dialpad/resources/fax_lines_resource.py new file mode 100644 index 0000000..d1345fa --- /dev/null +++ b/src/dialpad/resources/fax_lines_resource.py @@ -0,0 +1,27 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.faxline import CreateFaxNumberMessage, FaxNumberProto + + +class FaxLinesResource(DialpadResource): + """FaxLinesResource resource class + + Handles API operations for: + - /api/v2/faxline""" + + def assign(self, request_body: CreateFaxNumberMessage) -> FaxNumberProto: + """Fax Line -- Assign + + Assigns a fax line to a target. Target includes user and department. Depending on the chosen line type, the number will be taken from the company's reserved pool if there are available reserved numbers, otherwise numbers can be auto-assigned using a provided area code. + + Added on January 13, 2025 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/faxline', body=request_body) diff --git a/src/dialpad/resources/meeting_rooms_resource.py b/src/dialpad/resources/meeting_rooms_resource.py new file mode 100644 index 0000000..7648c38 --- /dev/null +++ b/src/dialpad/resources/meeting_rooms_resource.py @@ -0,0 +1,29 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.uberconference.room import RoomProto + + +class MeetingRoomsResource(DialpadResource): + """MeetingRoomsResource resource class + + Handles API operations for: + - /api/v2/conference/rooms""" + + def list(self, cursor: Optional[str] = None) -> Iterator[RoomProto]: + """Meeting Room -- List + + Lists all conference rooms. + + Requires scope: ``conference:read`` + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/conference/rooms', params={'cursor': cursor} + ) diff --git a/src/dialpad/resources/meetings_resource.py b/src/dialpad/resources/meetings_resource.py new file mode 100644 index 0000000..f58bd7b --- /dev/null +++ b/src/dialpad/resources/meetings_resource.py @@ -0,0 +1,34 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.uberconference.meeting import MeetingSummaryProto + + +class MeetingsResource(DialpadResource): + """MeetingsResource resource class + + Handles API operations for: + - /api/v2/conference/meetings""" + + def list( + self, cursor: Optional[str] = None, room_id: Optional[str] = None + ) -> Iterator[MeetingSummaryProto]: + """Meeting Summary -- List + + Lists summaries of meetings that have occured in the specified meeting room. + + Requires scope: ``conference:read`` + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + room_id: The meeting room's ID. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/conference/meetings', + params={'cursor': cursor, 'room_id': room_id}, + ) diff --git a/src/dialpad/resources/numbers_resource.py b/src/dialpad/resources/numbers_resource.py new file mode 100644 index 0000000..745e7ee --- /dev/null +++ b/src/dialpad/resources/numbers_resource.py @@ -0,0 +1,162 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.e164_format import FormatNumberResponse +from dialpad.schemas.number import ( + AssignNumberTargetGenericMessage, + AssignNumberTargetMessage, + NumberProto, + SwapNumberMessage, +) + + +class NumbersResource(DialpadResource): + """NumbersResource resource class + + Handles API operations for: + - /api/v2/numbers + - /api/v2/numbers/assign + - /api/v2/numbers/format + - /api/v2/numbers/swap + - /api/v2/numbers/{number} + - /api/v2/numbers/{number}/assign""" + + def assign(self, number: str, request_body: AssignNumberTargetMessage) -> NumberProto: + """Dialpad Number -- Assign + + Assigns a number to a target. Target includes user, department, office, room, callcenter, + callrouter, staffgroup, channel and coachinggroup. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. + + Added on May 26, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + number: A specific number to assign + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/numbers/{number}/assign', body=request_body + ) + + def auto_assign(self, request_body: AssignNumberTargetGenericMessage) -> NumberProto: + """Dialpad Number -- Auto-Assign + + Assigns a number to a target. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. Target includes user, department, office, room, callcenter, callrouter, + staffgroup, channel and coachinggroup. + + Added on November 18, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/numbers/assign', body=request_body) + + def format_number( + self, country_code: Optional[str] = None, number: Optional[str] = None + ) -> FormatNumberResponse: + """Phone String -- Reformat + + Used to convert local number to E.164 or E.164 to local format. + + Added on June 15, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + country_code: Country code in ISO 3166-1 alpha-2 format such as "US". Required when sending local formatted phone number + number: Phone number in local or E.164 format + + Returns: + A successful response""" + return self._request( + method='POST', + sub_path='/api/v2/numbers/format', + params={'country_code': country_code, 'number': number}, + ) + + def get(self, number: str) -> NumberProto: + """Dialpad Number -- Get + + Gets number details by number. + + Added on May 3, 2018 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + number: A phone number (e164 format). + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/numbers/{number}') + + def list( + self, cursor: Optional[str] = None, status: Optional[str] = None + ) -> Iterator[NumberProto]: + """Dialpad Number -- List + + Gets all numbers in your company. + + Added on May 3, 2018 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + status: Status to filter by. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/numbers', params={'cursor': cursor, 'status': status} + ) + + def swap(self, request_body: SwapNumberMessage) -> NumberProto: + """Dialpad Number -- Swap + + Swaps a target's primary number with a new one. + - If a specific number is provided (`type: 'provided_number'`), the target’s primary number is swapped with that number. The provided number must be available in the company’s reserved pool, + and the `reserve_pool` experiment must be enabled for the company. + - If an area code is provided (`type: 'area_code'`), an available number from that area code is assigned. + - If neither is provided (`type: 'auto'`), a number is automatically assigned — first from the company’s reserved pool (if available), otherwise from the target’s office area code. If no type is specified, 'auto' is used by default. + + Added on Mar 28, 2025 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/numbers/swap', body=request_body) + + def unassign(self, number: str, release: Optional[bool] = None) -> NumberProto: + """Dialpad Number -- Unassign + + Un-assigns a phone number from a target. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released. + + Added on Jan 28, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + number: A phone number (e164 format). + release: Releases the number (does not return it to the company reserved pool). + + Returns: + A successful response""" + return self._request( + method='DELETE', sub_path=f'/api/v2/numbers/{number}', params={'release': release} + ) diff --git a/src/dialpad/resources/oauth2_resource.py b/src/dialpad/resources/oauth2_resource.py new file mode 100644 index 0000000..95e550c --- /dev/null +++ b/src/dialpad/resources/oauth2_resource.py @@ -0,0 +1,73 @@ +from typing import Literal, Optional, Union + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.oauth import ( + AuthorizationCodeGrantBodySchema, + AuthorizeTokenResponseBodySchema, + RefreshTokenGrantBodySchema, +) + + +class OAuth2Resource(DialpadResource): + """OAuth2Resource resource class + + Handles API operations for: + - /oauth2/authorize + - /oauth2/deauthorize + - /oauth2/token""" + + def authorize_token( + self, + client_id: str, + redirect_uri: str, + code_challenge: Optional[str] = None, + code_challenge_method: Optional[Literal['S256', 'plain']] = None, + response_type: Optional[Literal['code']] = None, + scope: Optional[str] = None, + state: Optional[str] = None, + ) -> None: + """Token -- Authorize + + Initiate the OAuth flow to grant an application access to Dialpad resources on behalf of a user. + + Args: + client_id: The client_id of the OAuth app. + code_challenge: PKCE challenge value (hash commitment). + code_challenge_method: PKCE challenge method (hashing algorithm). + redirect_uri: The URI the user should be redirected back to after granting consent to the app. + response_type: The OAuth flow to perform. Must be 'code' (authorization code flow). + scope: Space-separated list of additional scopes that should be granted to the vended token. + state: Unpredictable token to prevent CSRF.""" + return self._request( + method='GET', + sub_path='/oauth2/authorize', + params={ + 'code_challenge_method': code_challenge_method, + 'code_challenge': code_challenge, + 'scope': scope, + 'response_type': response_type, + 'redirect_uri': redirect_uri, + 'client_id': client_id, + 'state': state, + }, + ) + + def deauthorize_token(self) -> None: + """Token -- Deauthorize + + Revokes oauth2 tokens for a given oauth app.""" + return self._request(method='POST', sub_path='/oauth2/deauthorize') + + def redeem_token( + self, request_body: Union[AuthorizationCodeGrantBodySchema, RefreshTokenGrantBodySchema] + ) -> AuthorizeTokenResponseBodySchema: + """Token -- Redeem + + Exchanges a temporary oauth code for an authorized access token. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/oauth2/token', body=request_body) diff --git a/src/dialpad/resources/offices_resource.py b/src/dialpad/resources/offices_resource.py new file mode 100644 index 0000000..1aefb2b --- /dev/null +++ b/src/dialpad/resources/offices_resource.py @@ -0,0 +1,319 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.coaching_team import CoachingTeamProto +from dialpad.schemas.group import ( + AddOperatorMessage, + CallCenterProto, + DepartmentProto, + OperatorCollection, + RemoveOperatorMessage, + UserOrRoomProto, +) +from dialpad.schemas.number import AssignNumberMessage, NumberProto, UnassignNumberMessage +from dialpad.schemas.office import ( + CreateOfficeMessage, + E911GetProto, + E911UpdateMessage, + OffDutyStatusesProto, + OfficeProto, + OfficeUpdateResponse, +) +from dialpad.schemas.plan import AvailableLicensesProto, PlanProto + + +class OfficesResource(DialpadResource): + """OfficesResource resource class + + Handles API operations for: + - /api/v2/offices + - /api/v2/offices/{id} + - /api/v2/offices/{id}/assign_number + - /api/v2/offices/{id}/e911 + - /api/v2/offices/{id}/offdutystatuses + - /api/v2/offices/{id}/operators + - /api/v2/offices/{id}/unassign_number + - /api/v2/offices/{office_id}/available_licenses + - /api/v2/offices/{office_id}/callcenters + - /api/v2/offices/{office_id}/departments + - /api/v2/offices/{office_id}/plan + - /api/v2/offices/{office_id}/teams""" + + def add_operator(self, id: int, request_body: AddOperatorMessage) -> UserOrRoomProto: + """Operator -- Add + + Adds an operator into office's mainline. + + Added on Sep 22, 2023 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's ID. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/offices/{id}/operators', body=request_body + ) + + def assign_number(self, id: int, request_body: AssignNumberMessage) -> NumberProto: + """Dialpad Number -- Assign + + Assigns a number to a office. The number will automatically be taken from the company's reserved pool if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. + + Added on March 19, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/offices/{id}/assign_number', body=request_body + ) + + def create(self, request_body: CreateOfficeMessage) -> OfficeUpdateResponse: + """Office -- POST Creates a secondary office. + + + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/offices', body=request_body) + + def get(self, id: int) -> OfficeProto: + """Office -- Get + + Gets an office by id. + + Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{id}') + + def get_billing_plan(self, office_id: int) -> PlanProto: + """Billing Plan -- Get + + Gets the plan for an office. + + Added on Mar 19, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + office_id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{office_id}/plan') + + def get_e911_address(self, id: int) -> E911GetProto: + """E911 Address -- Get + + Gets E911 address of the office by office id. + + Added on May 25, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{id}/e911') + + def list( + self, active_only: Optional[bool] = None, cursor: Optional[str] = None + ) -> Iterator[OfficeProto]: + """Office -- List + + Gets all the offices that are accessible using your api key. + + Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + active_only: Whether we only return active offices. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/offices', + params={'cursor': cursor, 'active_only': active_only}, + ) + + def list_available_licenses(self, office_id: int) -> AvailableLicensesProto: + """Licenses -- List Available + + Gets the available licenses for an office. + + Added on July 2, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + office_id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{office_id}/available_licenses') + + def list_call_centers( + self, office_id: int, cursor: Optional[str] = None + ) -> Iterator[CallCenterProto]: + """Call Centers -- List + + Gets all the call centers for an office. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + office_id: The office's id. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path=f'/api/v2/offices/{office_id}/callcenters', params={'cursor': cursor} + ) + + def list_coaching_teams( + self, office_id: int, cursor: Optional[str] = None + ) -> Iterator[CoachingTeamProto]: + """Coaching Team -- List + + Get a list of coaching teams of a office. Added on Jul 30th, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + office_id: The office's id. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path=f'/api/v2/offices/{office_id}/teams', params={'cursor': cursor} + ) + + def list_departments( + self, office_id: int, cursor: Optional[str] = None + ) -> Iterator[DepartmentProto]: + """Department -- List + + Gets all the departments for an office. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + office_id: The office's id. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path=f'/api/v2/offices/{office_id}/departments', params={'cursor': cursor} + ) + + def list_offduty_statuses(self, id: int) -> OffDutyStatusesProto: + """Off-Duty Status -- List + + Lists Off-Duty status values. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{id}/offdutystatuses') + + def list_operators(self, id: int) -> OperatorCollection: + """Operator -- List + + Gets mainline operators for an office. Added on May 1, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/offices/{id}/operators') + + def remove_operator(self, id: int, request_body: RemoveOperatorMessage) -> UserOrRoomProto: + """Operator -- Remove + + Removes an operator from office's mainline. + + Added on Sep 22, 2023 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's ID. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='DELETE', sub_path=f'/api/v2/offices/{id}/operators', body=request_body + ) + + def unassign_number(self, id: int, request_body: UnassignNumberMessage) -> NumberProto: + """Dialpad Number -- Unassign + + Un-assigns a phone number from a office mainline. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released. + + Added on March 19, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/offices/{id}/unassign_number', body=request_body + ) + + def update_e911_address(self, id: int, request_body: E911UpdateMessage) -> E911GetProto: + """E911 Address -- Update + + Update E911 address of the given office. + + Added on May 25, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The office's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PUT', sub_path=f'/api/v2/offices/{id}/e911', body=request_body) diff --git a/src/dialpad/resources/recording_share_links_resource.py b/src/dialpad/resources/recording_share_links_resource.py new file mode 100644 index 0000000..438b25c --- /dev/null +++ b/src/dialpad/resources/recording_share_links_resource.py @@ -0,0 +1,81 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.recording_share_link import ( + CreateRecordingShareLink, + RecordingShareLink, + UpdateRecordingShareLink, +) + + +class RecordingShareLinksResource(DialpadResource): + """RecordingShareLinksResource resource class + + Handles API operations for: + - /api/v2/recordingsharelink + - /api/v2/recordingsharelink/{id}""" + + def create(self, request_body: CreateRecordingShareLink) -> RecordingShareLink: + """Recording Sharelink -- Create + + Creates a recording share link. + + Added on Aug 26, 2021 for API v2. + + Rate limit: 100 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/recordingsharelink', body=request_body) + + def delete(self, id: str) -> RecordingShareLink: + """Recording Sharelink -- Delete + + Deletes a recording share link by id. + + Added on Aug 26, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The recording share link's ID. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/recordingsharelink/{id}') + + def get(self, id: str) -> RecordingShareLink: + """Recording Sharelink -- Get + + Gets a recording share link by id. + + Added on Aug 26, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The recording share link's ID. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/recordingsharelink/{id}') + + def update(self, id: str, request_body: UpdateRecordingShareLink) -> RecordingShareLink: + """Recording Sharelink -- Update + + Updates a recording share link by id. + + Added on Aug 26, 2021 for API v2. + + Rate limit: 100 per minute. + + Args: + id: The recording share link's ID. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PUT', sub_path=f'/api/v2/recordingsharelink/{id}', body=request_body + ) diff --git a/src/dialpad/resources/rooms_resource.py b/src/dialpad/resources/rooms_resource.py new file mode 100644 index 0000000..11082a9 --- /dev/null +++ b/src/dialpad/resources/rooms_resource.py @@ -0,0 +1,215 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.deskphone import DeskPhone +from dialpad.schemas.number import AssignNumberMessage, NumberProto, UnassignNumberMessage +from dialpad.schemas.room import ( + CreateInternationalPinProto, + CreateRoomMessage, + InternationalPinProto, + RoomProto, + UpdateRoomMessage, +) + + +class RoomsResource(DialpadResource): + """RoomsResource resource class + + Handles API operations for: + - /api/v2/rooms + - /api/v2/rooms/international_pin + - /api/v2/rooms/{id} + - /api/v2/rooms/{id}/assign_number + - /api/v2/rooms/{id}/unassign_number + - /api/v2/rooms/{parent_id}/deskphones + - /api/v2/rooms/{parent_id}/deskphones/{id}""" + + def assign_number(self, id: int, request_body: AssignNumberMessage) -> NumberProto: + """Dialpad Number -- Assign + + Assigns a number to a room. The number will automatically be taken from the company's reserved block if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. + + Added on March 19, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The room's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/rooms/{id}/assign_number', body=request_body + ) + + def assign_phone_pin(self, request_body: CreateInternationalPinProto) -> InternationalPinProto: + """Room Phone -- Assign PIN + + Assigns a PIN for making international calls from rooms + + When PIN protected international calls are enabled for the company, a PIN is required to make international calls from room phones. + + Added on Aug 16, 2018 for API v2. + + Requires a company admin API key. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path='/api/v2/rooms/international_pin', body=request_body + ) + + def create(self, request_body: CreateRoomMessage) -> RoomProto: + """Room -- Create + + Creates a new room. + + Added on Mar 8, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/rooms', body=request_body) + + def delete(self, id: int) -> RoomProto: + """Room -- Delete + + Deletes a room by id. + + Added on Mar 8, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The room's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/rooms/{id}') + + def delete_room_phone(self, id: str, parent_id: int) -> None: + """Room Phone -- Delete + + Deletes a room desk phone by id. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The desk phone's id. + parent_id: The room's id. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/rooms/{parent_id}/deskphones/{id}') + + def get(self, id: int) -> RoomProto: + """Room -- Get + + Gets a room by id. + + Added on Aug 13, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The room's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/rooms/{id}') + + def get_room_phone(self, id: str, parent_id: int) -> DeskPhone: + """Room Phone -- Get + + Gets a room desk phone by id. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The desk phone's id. + parent_id: The room's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/rooms/{parent_id}/deskphones/{id}') + + def list( + self, cursor: Optional[str] = None, office_id: Optional[int] = None + ) -> Iterator[RoomProto]: + """Room -- List + + Gets all rooms in your company, optionally filtering by office. + + Added on Aug 13, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + office_id: The office's id. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/rooms', params={'cursor': cursor, 'office_id': office_id} + ) + + def list_room_phones(self, parent_id: int) -> Iterator[DeskPhone]: + """Room Phone -- List + + Gets all desk phones under a room. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + parent_id: The room's id. + + Returns: + An iterator of items from A successful response""" + return self._iter_request(method='GET', sub_path=f'/api/v2/rooms/{parent_id}/deskphones') + + def partial_update(self, id: int, request_body: UpdateRoomMessage) -> RoomProto: + """Room -- Update + + Updates room details by id. + + Added on Mar 8, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The room's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/rooms/{id}', body=request_body) + + def unassign_number(self, id: int, request_body: UnassignNumberMessage) -> NumberProto: + """Dialpad Number -- Unassign + + Un-assigns a phone number from a room. The number will be returned to the company's reserved pool if there is one. Otherwise the number will be released. + + Added on March 19, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The room's id. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/rooms/{id}/unassign_number', body=request_body + ) diff --git a/src/dialpad/resources/schedule_reports_resource.py b/src/dialpad/resources/schedule_reports_resource.py new file mode 100644 index 0000000..af445e4 --- /dev/null +++ b/src/dialpad/resources/schedule_reports_resource.py @@ -0,0 +1,106 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.schedule_reports import ( + ProcessScheduleReportsMessage, + ScheduleReportsStatusEventSubscriptionProto, +) + + +class ScheduleReportsResource(DialpadResource): + """ScheduleReportsResource resource class + + Handles API operations for: + - /api/v2/schedulereports + - /api/v2/schedulereports/{id}""" + + def create( + self, request_body: ProcessScheduleReportsMessage + ) -> ScheduleReportsStatusEventSubscriptionProto: + """schedule reports -- Create + + Creates a schedule reports subscription for your company. An endpoint_id is required in order to receive the event payload and can be obtained via websockets or webhooks. A schedule reports is a mechanism to schedule daily, weekly or monthly record and statistics reports. + + Added on Jun 17, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/schedulereports', body=request_body) + + def delete(self, id: int) -> ScheduleReportsStatusEventSubscriptionProto: + """Schedule reports -- Delete + + Deletes a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports. + + Added on Jul 6, 2022 for API v2 + + Rate limit: 1200 per minute. + + Args: + id: The schedule reports subscription's ID. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/schedulereports/{id}') + + def get(self, id: int) -> ScheduleReportsStatusEventSubscriptionProto: + """Schedule reports -- Get + + Gets a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports. + + Added on Jul 6, 2022 for API v2 + + Rate limit: 1200 per minute. + + Args: + id: The schedule reports subscription's ID. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/schedulereports/{id}') + + def list( + self, cursor: Optional[str] = None + ) -> Iterator[ScheduleReportsStatusEventSubscriptionProto]: + """Schedule reports -- List + + Lists all schedule reports subscription for a company. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports. + + Added on Jul 6, 2022 for API v2 + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/schedulereports', params={'cursor': cursor} + ) + + def partial_update( + self, id: int, request_body: ProcessScheduleReportsMessage + ) -> ScheduleReportsStatusEventSubscriptionProto: + """Schedule reports -- Update + + Updates a schedule report subscription by id. A schedule report is a mechanism to schedule daily, weekly or monthly record and statistics reports. + + Added on Jul 6, 2022 for API v2 + + Rate limit: 1200 per minute. + + Args: + id: The schedule reports subscription's ID. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/schedulereports/{id}', body=request_body + ) diff --git a/src/dialpad/resources/sms_event_subscriptions_resource.py b/src/dialpad/resources/sms_event_subscriptions_resource.py new file mode 100644 index 0000000..6cb05fa --- /dev/null +++ b/src/dialpad/resources/sms_event_subscriptions_resource.py @@ -0,0 +1,142 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.sms_event_subscription import ( + CreateSmsEventSubscription, + SmsEventSubscriptionProto, + UpdateSmsEventSubscription, +) + + +class SmsEventSubscriptionsResource(DialpadResource): + """SmsEventSubscriptionsResource resource class + + Handles API operations for: + - /api/v2/subscriptions/sms + - /api/v2/subscriptions/sms/{id}""" + + def create(self, request_body: CreateSmsEventSubscription) -> SmsEventSubscriptionProto: + """SMS Event -- Create + + Creates a SMS event subscription. A webhook_id is required so that we know to which url the events shall be sent. A SMS direction is also required in order to limit the direction for which SMS events are sent. Use 'all' to get SMS events for all directions. A target_type and target_id may optionally be provided to scope the events only to SMS to/from that target. + + See https://developers.dialpad.com/docs/sms-events for details on how SMS events work, including the payload structure and payload examples. + + NOTE: **To include the MESSAGE CONTENT in SMS events, your API key needs to have the + "message_content_export" OAuth scope for when a target is specified in this API and/or + "message_content_export:all" OAuth scope for when no target is specified.** + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Added on April 9th, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/subscriptions/sms', body=request_body) + + def delete(self, id: int) -> SmsEventSubscriptionProto: + """SMS Event -- Delete + + Deletes a SMS event subscription by id. + + Added on April 9th, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/subscriptions/sms/{id}') + + def get(self, id: int) -> SmsEventSubscriptionProto: + """SMS Event -- Get + + Gets a SMS event subscription by id. + + Added on April 9th, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/subscriptions/sms/{id}') + + def list( + self, + cursor: Optional[str] = None, + target_id: Optional[int] = None, + target_type: Optional[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] = None, + ) -> Iterator[SmsEventSubscriptionProto]: + """SMS Event -- List + + Gets a list of all the SMS event subscriptions of a company or of a target. + + Added on April 9th, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + target_id: The target's id. + target_type: Target's type. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/subscriptions/sms', + params={'cursor': cursor, 'target_type': target_type, 'target_id': target_id}, + ) + + def partial_update( + self, id: int, request_body: UpdateSmsEventSubscription + ) -> SmsEventSubscriptionProto: + """SMS Event -- Update + + Updates a SMS event subscription by id. + + Added on April 9th, 2021 for API v2. + + NOTE: See https://developers.dialpad.com/v1.0-archive/reference for APIs that can operate on subscriptions that were created via the deprecated APIs. + + Rate limit: 1200 per minute. + + Args: + id: The event subscription's ID, which is generated after creating an event subscription successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/subscriptions/sms/{id}', body=request_body + ) diff --git a/src/dialpad/resources/sms_resource.py b/src/dialpad/resources/sms_resource.py new file mode 100644 index 0000000..fa1be91 --- /dev/null +++ b/src/dialpad/resources/sms_resource.py @@ -0,0 +1,29 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.sms import SendSMSMessage, SMSProto + + +class SmsResource(DialpadResource): + """SmsResource resource class + + Handles API operations for: + - /api/v2/sms""" + + def send(self, request_body: SendSMSMessage) -> SMSProto: + """SMS -- Send + + Sends an SMS message to a phone number or to a Dialpad channel on behalf of a user. + + Added on Dec 18, 2019 for API v2. + + Tier 0 Rate limit: 100 per minute. + + Tier 1 Rate limit: 800 per minute. + + + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/sms', body=request_body) diff --git a/src/dialpad/resources/stats_resource.py b/src/dialpad/resources/stats_resource.py new file mode 100644 index 0000000..4c76989 --- /dev/null +++ b/src/dialpad/resources/stats_resource.py @@ -0,0 +1,44 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.stats import ProcessingProto, ProcessStatsMessage, StatsProto + + +class StatsResource(DialpadResource): + """StatsResource resource class + + Handles API operations for: + - /api/v2/stats + - /api/v2/stats/{id}""" + + def get_result(self, id: str) -> StatsProto: + """Stats -- Get Result + + Gets the progress and result of a statistics request. + + Added on May 3, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: Request ID returned by a POST /stats request. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/stats/{id}') + + def initiate_processing(self, request_body: ProcessStatsMessage) -> ProcessingProto: + """Stats -- Initiate Processing + + Begins processing statistics asynchronously, returning a request id to get the status and retrieve the results by calling GET /stats/{request_id}. + + Stats for the whole company will be processed by default. An office_id can be provided to limit stats to a single office. A target_id and target_type can be provided to limit stats to a single target. + + Added on May 3, 2018 for API v2. + + Rate limit: 200 per hour. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/stats', body=request_body) diff --git a/src/dialpad/resources/transcripts_resource.py b/src/dialpad/resources/transcripts_resource.py new file mode 100644 index 0000000..ab64db4 --- /dev/null +++ b/src/dialpad/resources/transcripts_resource.py @@ -0,0 +1,42 @@ +from dialpad.resources.base import DialpadResource +from dialpad.schemas.transcript import TranscriptProto, TranscriptUrlProto + + +class TranscriptsResource(DialpadResource): + """TranscriptsResource resource class + + Handles API operations for: + - /api/v2/transcripts/{call_id} + - /api/v2/transcripts/{call_id}/url""" + + def get(self, call_id: int) -> TranscriptProto: + """Call Transcript -- Get + + Gets the Dialpad AI transcript of a call, including moments. + + Added on Dec 18, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + call_id: The call's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/transcripts/{call_id}') + + def get_url(self, call_id: int) -> TranscriptUrlProto: + """Call Transcript -- Get URL + + Gets the transcript url of a call. + + Added on June 9, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + call_id: The call's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/transcripts/{call_id}/url') diff --git a/src/dialpad/resources/user_devices_resource.py b/src/dialpad/resources/user_devices_resource.py new file mode 100644 index 0000000..e49c4a1 --- /dev/null +++ b/src/dialpad/resources/user_devices_resource.py @@ -0,0 +1,49 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.userdevice import UserDeviceProto + + +class UserDevicesResource(DialpadResource): + """UserDevicesResource resource class + + Handles API operations for: + - /api/v2/userdevices + - /api/v2/userdevices/{id}""" + + def get(self, id: str) -> UserDeviceProto: + """User Device -- Get + + Gets a device by ID. + + Added on Feb 4, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The device's id. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/userdevices/{id}') + + def list( + self, cursor: Optional[str] = None, user_id: Optional[str] = None + ) -> Iterator[UserDeviceProto]: + """User Device -- List + + Lists the devices for a specific user. + + Added on Feb 4, 2020 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + user_id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/userdevices', params={'cursor': cursor, 'user_id': user_id} + ) diff --git a/src/dialpad/resources/users_resource.py b/src/dialpad/resources/users_resource.py new file mode 100644 index 0000000..e3b4ad9 --- /dev/null +++ b/src/dialpad/resources/users_resource.py @@ -0,0 +1,452 @@ +from typing import Iterator, Literal, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.call import ( + ActiveCallProto, + InitiateCallMessage, + InitiatedCallProto, + ToggleViMessage, + ToggleViProto, + UpdateActiveCallMessage, +) +from dialpad.schemas.caller_id import CallerIdProto, SetCallerIdMessage +from dialpad.schemas.deskphone import DeskPhone +from dialpad.schemas.number import AssignNumberMessage, NumberProto, UnassignNumberMessage +from dialpad.schemas.office import E911GetProto +from dialpad.schemas.screen_pop import InitiateScreenPopMessage, InitiateScreenPopResponse +from dialpad.schemas.user import ( + CreateUserMessage, + E911UpdateMessage, + MoveOfficeMessage, + PersonaProto, + SetStatusMessage, + SetStatusProto, + ToggleDNDMessage, + ToggleDNDProto, + UpdateUserMessage, + UserProto, +) + + +class UsersResource(DialpadResource): + """UsersResource resource class + + Handles API operations for: + - /api/v2/users + - /api/v2/users/{id} + - /api/v2/users/{id}/activecall + - /api/v2/users/{id}/assign_number + - /api/v2/users/{id}/caller_id + - /api/v2/users/{id}/e911 + - /api/v2/users/{id}/initiate_call + - /api/v2/users/{id}/move_office + - /api/v2/users/{id}/personas + - /api/v2/users/{id}/screenpop + - /api/v2/users/{id}/status + - /api/v2/users/{id}/togglednd + - /api/v2/users/{id}/togglevi + - /api/v2/users/{id}/unassign_number + - /api/v2/users/{parent_id}/deskphones + - /api/v2/users/{parent_id}/deskphones/{id}""" + + def assign_number(self, id: int, request_body: AssignNumberMessage) -> NumberProto: + """Dialpad Number -- Assign + + Assigns a number to a user. The number will automatically be taken from the company's reserved block if there are reserved numbers, otherwise a number will be auto-assigned from the provided area code. + + Added on May 3, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/users/{id}/assign_number', body=request_body + ) + + def create(self, request_body: CreateUserMessage) -> UserProto: + """User -- Create + + Creates a new user. + + Added on March 22, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/users', body=request_body) + + def delete(self, id: str) -> UserProto: + """User -- Delete + + Deletes a user by id. + + Added on May 11, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/users/{id}') + + def delete_deskphone(self, id: str, parent_id: int) -> None: + """Desk Phone -- Delete + + Deletes a user desk phone by id. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The desk phone's id. + parent_id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/users/{parent_id}/deskphones/{id}') + + def get(self, id: str) -> UserProto: + """User -- Get + + Gets a user by id. + + Added on March 22, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/users/{id}') + + def get_caller_id(self, id: str) -> CallerIdProto: + """Caller ID -- Get + + List all available Caller IDs and the active Called ID for a determined User id + + Added on Aug 3, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/users/{id}/caller_id') + + def get_deskphone(self, id: str, parent_id: int) -> DeskPhone: + """Desk Phone -- Get + + Gets a user desk phone by id. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The desk phone's id. + parent_id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/users/{parent_id}/deskphones/{id}') + + def get_e911_address(self, id: int) -> E911GetProto: + """E911 Address -- Get + + Gets E911 address of the user by user id. + + Added on May 25, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/users/{id}/e911') + + def initiate_call(self, id: str, request_body: InitiateCallMessage) -> InitiatedCallProto: + """Call -- Initiate + + Causes a user's native Dialpad application to initiate an outbound call. Added on Nov 18, 2019 for API v2. + + Rate limit: 5 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/users/{id}/initiate_call', body=request_body + ) + + def list( + self, + company_admin: Optional[bool] = None, + cursor: Optional[str] = None, + email: Optional[str] = None, + number: Optional[str] = None, + state: Optional[ + Literal['active', 'all', 'cancelled', 'deleted', 'pending', 'suspended'] + ] = None, + ) -> Iterator[UserProto]: + """User -- List + + Gets company users, optionally filtering by email. + + NOTE: The `limit` parameter has been soft-deprecated. Please omit the `limit` parameter, or reduce it to `100` or less. + + - Limit values of greater than `100` will only produce a page size of `100`, and a + `400 Bad Request` response will be produced 20% of the time in an effort to raise visibility of side-effects that might otherwise go un-noticed by solutions that had assumed a larger page size. + + - The `cursor` value is provided in the API response, and can be passed as a parameter to retrieve subsequent pages of results. + + Added on March 22, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + company_admin: If provided, filter results by the specified value to return only company admins or only non-company admins. + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + email: The user's email. + number: The user's phone number. + state: Filter results by the specified user state (e.g. active, suspended, deleted) + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', + sub_path='/api/v2/users', + params={ + 'cursor': cursor, + 'state': state, + 'company_admin': company_admin, + 'email': email, + 'number': number, + }, + ) + + def list_deskphones(self, parent_id: int) -> Iterator[DeskPhone]: + """Desk Phone -- List + + Gets all desk phones under a user. Added on May 17, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + parent_id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + An iterator of items from A successful response""" + return self._iter_request(method='GET', sub_path=f'/api/v2/users/{parent_id}/deskphones') + + def list_personas(self, id: str) -> Iterator[PersonaProto]: + """Persona -- List + + Provides a list of personas for a user. + + A persona is a target that a user can make calls from. The receiver of the call will see the details of the persona rather than the user. + + Added on February 12, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + + Returns: + An iterator of items from A successful response""" + return self._iter_request(method='GET', sub_path=f'/api/v2/users/{id}/personas') + + def move_office(self, id: str, request_body: MoveOfficeMessage) -> UserProto: + """User -- Switch Office + + Moves the user to a different office. For international offices only, all of the user's numbers will be unassigned and a new number will be assigned except when the user only has internal numbers starting with 803 -- then the numbers will remain unchanged. Admin can also assign numbers via the user assign number API after. Only supported on paid accounts and there must be enough licenses to transfer the user to the destination office. + + Added on May 31, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/users/{id}/move_office', body=request_body + ) + + def partial_update(self, id: str, request_body: UpdateUserMessage) -> UserProto: + """User -- Update + + Updates the provided fields for an existing user. + + Added on March 22, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/users/{id}', body=request_body) + + def set_caller_id(self, id: str, request_body: SetCallerIdMessage) -> CallerIdProto: + """Caller ID -- POST + + Set Caller ID for a determined User id. + + Added on Aug 3, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path=f'/api/v2/users/{id}/caller_id', body=request_body) + + def set_e911_address(self, id: int, request_body: E911UpdateMessage) -> E911GetProto: + """E911 Address -- Update + + Update E911 address of the given user. + + Added on May 25, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PUT', sub_path=f'/api/v2/users/{id}/e911', body=request_body) + + def toggle_active_call_recording( + self, id: int, request_body: UpdateActiveCallMessage + ) -> ActiveCallProto: + """Call Recording -- Toggle + + Turns call recording on or off for a user's active call. + + Added on Nov 18, 2019 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/users/{id}/activecall', body=request_body + ) + + def toggle_active_call_vi(self, id: int, request_body: ToggleViMessage) -> ToggleViProto: + """Call VI -- Toggle + + Turns call vi on or off for a user's active call. Added on May 4, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/users/{id}/togglevi', body=request_body) + + def toggle_dnd(self, id: str, request_body: ToggleDNDMessage) -> ToggleDNDProto: + """Do Not Disturb -- Toggle + + Toggle DND status on or off for the given user. + + Added on Oct 14, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='PATCH', sub_path=f'/api/v2/users/{id}/togglednd', body=request_body + ) + + def trigger_screenpop( + self, id: int, request_body: InitiateScreenPopMessage + ) -> InitiateScreenPopResponse: + """Screen-pop -- Trigger + + Initiates screen pop for user device. Requires the "screen_pop" scope. + + Requires scope: ``screen_pop`` + + Rate limit: 5 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path=f'/api/v2/users/{id}/screenpop', body=request_body) + + def unassign_number(self, id: int, request_body: UnassignNumberMessage) -> NumberProto: + """Dialpad Number -- Unassign + + Un-assigns a phone number from a user. The number will be returned to the company's reserved block if there is one. Otherwise the number will be released. + + Added on May 3, 2018 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request( + method='POST', sub_path=f'/api/v2/users/{id}/unassign_number', body=request_body + ) + + def update_user_status(self, id: int, request_body: SetStatusMessage) -> SetStatusProto: + """User Status -- Update + + Update user's status. Returns the user's status if the user exists. + + Rate limit: 1200 per minute. + + Args: + id: The user's id. ('me' can be used if you are using a user level API key) + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/users/{id}/status', body=request_body) diff --git a/src/dialpad/resources/webhooks_resource.py b/src/dialpad/resources/webhooks_resource.py new file mode 100644 index 0000000..2998bc3 --- /dev/null +++ b/src/dialpad/resources/webhooks_resource.py @@ -0,0 +1,95 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.webhook import CreateWebhook, UpdateWebhook, WebhookProto + + +class WebhooksResource(DialpadResource): + """WebhooksResource resource class + + Handles API operations for: + - /api/v2/webhooks + - /api/v2/webhooks/{id}""" + + def create(self, request_body: CreateWebhook) -> WebhookProto: + """Webhook -- Create + + Creates a new webhook for your company. + + An unique webhook ID will be generated when successfully creating a webhook. A webhook ID is to be required when creating event subscriptions. One webhook ID can be shared between multiple event subscriptions. When triggered, events will be sent to the provided hook_url under webhook. If a secret is provided, the webhook events will be encoded and signed in the JWT format using the shared secret with the HS256 algorithm. The JWT payload should be decoded and the signature verified to ensure that the event came from Dialpad. If no secret is provided, unencoded events will be sent in the JSON format. It is recommended to provide a secret so that you can verify the authenticity of the event. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 100 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/webhooks', body=request_body) + + def delete(self, id: int) -> WebhookProto: + """Webhook -- Delete + + Deletes a webhook by id. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The webhook's ID, which is generated after creating a webhook successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/webhooks/{id}') + + def get(self, id: int) -> WebhookProto: + """Webhook -- Get + + Gets a webhook by id. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The webhook's ID, which is generated after creating a webhook successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/webhooks/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[WebhookProto]: + """Webhook -- List + + Gets a list of all the webhooks that are associated with the company. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request(method='GET', sub_path='/api/v2/webhooks', params={'cursor': cursor}) + + def partial_update(self, id: str, request_body: UpdateWebhook) -> WebhookProto: + """Webhook -- Update + + Updates a webhook by id. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The webhook's ID, which is generated after creating a webhook successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/webhooks/{id}', body=request_body) diff --git a/src/dialpad/resources/websockets_resource.py b/src/dialpad/resources/websockets_resource.py new file mode 100644 index 0000000..dc5a7d0 --- /dev/null +++ b/src/dialpad/resources/websockets_resource.py @@ -0,0 +1,101 @@ +from typing import Iterator, Optional + +from dialpad.resources.base import DialpadResource +from dialpad.schemas.websocket import ( + CreateWebsocket, + UpdateWebsocket, + WebsocketProto, +) + + +class WebsocketsResource(DialpadResource): + """WebsocketsResource resource class + + Handles API operations for: + - /api/v2/websockets + - /api/v2/websockets/{id}""" + + def create(self, request_body: CreateWebsocket) -> WebsocketProto: + """Websocket -- Create + + Creates a new websocket for your company. + + A unique websocket ID will be generated when successfully creating a websocket. A websocket ID is to be required when creating event subscriptions. One websocket ID can be shared between multiple event subscriptions. When triggered, events will be accessed through provided websocket_url under websocket. The url will be expired after 1 hour. Please use the GET websocket API to regenerate url rather than creating new ones. If a secret is provided, the websocket events will be encoded and signed in the JWT format using the shared secret with the HS256 algorithm. The JWT payload should be decoded and the signature verified to ensure that the event came from Dialpad. If no secret is provided, unencoded events will be sent in the JSON format. It is recommended to provide a secret so that you can verify the authenticity of the event. + + Added on April 5th, 2022 for API v2. + + Rate limit: 250 per minute. + + Args: + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='POST', sub_path='/api/v2/websockets', body=request_body) + + def delete(self, id: int) -> WebsocketProto: + """Websocket -- Delete + + Deletes a websocket by id. + + Added on April 2nd, 2021 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The websocket's ID, which is generated after creating a websocket successfully. + + Returns: + A successful response""" + return self._request(method='DELETE', sub_path=f'/api/v2/websockets/{id}') + + def get(self, id: int) -> WebsocketProto: + """Websocket -- Get + + Gets a websocket by id. + + Added on April 5th, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The websocket's ID, which is generated after creating a websocket successfully. + + Returns: + A successful response""" + return self._request(method='GET', sub_path=f'/api/v2/websockets/{id}') + + def list(self, cursor: Optional[str] = None) -> Iterator[WebsocketProto]: + """Websocket -- List + + Gets a list of all the websockets that are associated with the company. + + Added on April 5th, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + cursor: A token used to return the next page of a previous request. Use the cursor provided in the previous response. + + Returns: + An iterator of items from A successful response""" + return self._iter_request( + method='GET', sub_path='/api/v2/websockets', params={'cursor': cursor} + ) + + def partial_update(self, id: int, request_body: UpdateWebsocket) -> WebsocketProto: + """Websocket -- Update + + Updates a websocket by id. + + Added on April 5th, 2022 for API v2. + + Rate limit: 1200 per minute. + + Args: + id: The websocket's ID, which is generated after creating a websocket successfully. + request_body: The request body. + + Returns: + A successful response""" + return self._request(method='PATCH', sub_path=f'/api/v2/websockets/{id}', body=request_body) diff --git a/src/dialpad/schemas/__init__.py b/src/dialpad/schemas/__init__.py new file mode 100644 index 0000000..b29ae4b --- /dev/null +++ b/src/dialpad/schemas/__init__.py @@ -0,0 +1 @@ +# This is an auto-generated schema package. Please do not edit it directly. diff --git a/src/dialpad/schemas/access_control_policies.py b/src/dialpad/schemas/access_control_policies.py new file mode 100644 index 0000000..e572528 --- /dev/null +++ b/src/dialpad/schemas/access_control_policies.py @@ -0,0 +1,203 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.user import UserProto + + +class AssignmentPolicyMessage(TypedDict): + """Policy assignment message.""" + + target_id: NotRequired[int] + 'Required if the policy is associated with a target (Office or Contact Center). Not required for a company level policy.' + target_type: NotRequired[Literal['callcenter', 'company', 'office']] + 'Policy permissions applied at this target level. Defaults to company target type.' + user_id: int + "The user's id to be assigned to the policy." + + +class CreatePolicyMessage(TypedDict): + """Create access control policy message.""" + + description: NotRequired[str] + '[single-line only]\n\nOptional description for the policy. Max 200 characters.' + name: str + '[single-line only]\n\nA human-readable display name for the policy. Max 50 characters.' + owner_id: int + 'Owner for this policy i.e company admin.' + permission_sets: list[ + Literal[ + 'agent_settings_write', + 'agents_admins_manage_agents_settings_write', + 'agents_admins_skill_level_write', + 'auto_call_recording_and_transcription_settings_write', + 'business_hours_write', + 'call_blocking_spam_prevention_settings_write', + 'call_dispositions_settings_write', + 'call_routing_hours_settings_write', + 'cc_call_settings_write', + 'chrome_extension_compliance_settings_write', + 'csat_surveys_write', + 'dashboard_and_alerts_write', + 'dialpad_ai_settings_write', + 'holiday_hours_settings_write', + 'integrations_settings_write', + 'name_language_description_settings_write', + 'number_settings_write', + 'supervisor_settings_write', + ] + ] + 'List of permission associated with this policy.' + target_type: NotRequired[Literal['callcenter', 'company', 'office']] + 'Policy permissions applied at this target level. Defaults to company target type.' + + +class PolicyProto(TypedDict): + """API custom access control policy proto definition.""" + + company_id: int + "The company's id to which this policy belongs to." + date_created: NotRequired[str] + 'A timestamp indicating when this custom policy was created.' + date_updated: NotRequired[str] + 'A timestamp indicating when this custom policy was last modified.' + description: NotRequired[str] + '[single-line only]\n\nDescription for the custom policy.' + id: int + 'The API custom policy ID.' + name: str + '[single-line only]\n\nA human-readable display name for the custom policy name.' + owner_id: int + 'Target that created this policy i.e company admin.' + permission_sets: list[ + Literal[ + 'agent_settings_read', + 'agent_settings_write', + 'agents_admins_manage_agents_settings_read', + 'agents_admins_manage_agents_settings_write', + 'agents_admins_skill_level_read', + 'agents_admins_skill_level_write', + 'auto_call_recording_and_transcription_settings_read', + 'auto_call_recording_and_transcription_settings_write', + 'business_hours_read', + 'business_hours_write', + 'call_blocking_spam_prevention_settings_read', + 'call_blocking_spam_prevention_settings_write', + 'call_dispositions_settings_read', + 'call_dispositions_settings_write', + 'call_routing_hours_settings_read', + 'call_routing_hours_settings_write', + 'cc_call_settings_read', + 'cc_call_settings_write', + 'chrome_extension_compliance_settings_read', + 'chrome_extension_compliance_settings_write', + 'csat_surveys_read', + 'csat_surveys_write', + 'dashboard_and_alerts_read', + 'dashboard_and_alerts_write', + 'dialpad_ai_settings_read', + 'dialpad_ai_settings_write', + 'holiday_hours_settings_read', + 'holiday_hours_settings_write', + 'integrations_settings_read', + 'integrations_settings_write', + 'name_language_description_settings_read', + 'name_language_description_settings_write', + 'number_settings_read', + 'number_settings_write', + 'supervisor_settings_read', + 'supervisor_settings_write', + ] + ] + 'List of permission associated with this custom policy.' + state: Literal['active', 'deleted'] + 'Policy state. ex. active or deleted.' + target_type: NotRequired[Literal['callcenter', 'company', 'office']] + 'Target level at which the policy permissions are applied. Defaults to company' + + +class PoliciesCollection(TypedDict): + """Collection of custom policies.""" + + cursor: NotRequired[str] + 'A cursor string that can be used to fetch the subsequent page.' + items: NotRequired[list[PolicyProto]] + 'A list containing the first page of results.' + + +class PolicyTargetProto(TypedDict): + """TypedDict representation of the PolicyTargetProto schema.""" + + target_id: int + 'All targets associated with the policy.' + target_type: NotRequired[Literal['callcenter', 'company', 'office']] + 'Policy permissions applied at this target level. Defaults to company target type.' + + +class PolicyAssignmentProto(TypedDict): + """TypedDict representation of the PolicyAssignmentProto schema.""" + + policy_targets: NotRequired[list[PolicyTargetProto]] + 'Policy targets associated with the role.' + user: NotRequired[UserProto] + 'The user associated to the role.' + + +class PolicyAssignmentCollection(TypedDict): + """Collection of policy assignments.""" + + cursor: NotRequired[str] + 'A cursor string that can be used to fetch the subsequent page.' + items: NotRequired[list[PolicyAssignmentProto]] + 'A list containing the first page of results.' + + +class UnassignmentPolicyMessage(TypedDict): + """Policy unassignment message.""" + + target_id: NotRequired[int] + 'Required if the policy is associated with a target (Office or Contact Center). Not required for a company level policy or if unassign_all is True.' + target_type: NotRequired[Literal['callcenter', 'company', 'office']] + 'Policy permissions applied at this target level. Defaults to company target type.' + unassign_all: NotRequired[bool] + 'Unassign all associated target groups from the user for a policy.' + user_id: int + "The user's id to be assigned to the policy." + + +class UpdatePolicyMessage(TypedDict): + """Update policy message.""" + + description: NotRequired[str] + '[single-line only]\n\nOptional description for the policy.' + name: NotRequired[str] + '[single-line only]\n\nA human-readable display name for the policy.' + permission_sets: NotRequired[ + list[ + Literal[ + 'agent_settings_write', + 'agents_admins_manage_agents_settings_write', + 'agents_admins_skill_level_write', + 'auto_call_recording_and_transcription_settings_write', + 'business_hours_write', + 'call_blocking_spam_prevention_settings_write', + 'call_dispositions_settings_write', + 'call_routing_hours_settings_write', + 'cc_call_settings_write', + 'chrome_extension_compliance_settings_write', + 'csat_surveys_write', + 'dashboard_and_alerts_write', + 'dialpad_ai_settings_write', + 'holiday_hours_settings_write', + 'integrations_settings_write', + 'name_language_description_settings_write', + 'number_settings_write', + 'supervisor_settings_write', + ] + ] + ] + 'List of permission associated with this policy.' + state: NotRequired[Literal['active']] + 'Restore a deleted policy.' + user_id: NotRequired[int] + 'user id updating this policy. Must be a company admin' diff --git a/src/dialpad/schemas/agent_status_event_subscription.py b/src/dialpad/schemas/agent_status_event_subscription.py new file mode 100644 index 0000000..81aafae --- /dev/null +++ b/src/dialpad/schemas/agent_status_event_subscription.py @@ -0,0 +1,52 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class AgentStatusEventSubscriptionProto(TypedDict): + """Agent-status event subscription.""" + + agent_type: Literal['callcenter'] + 'The agent type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the this agent status event subscription is enabled.' + id: NotRequired[int] + "The event subscription's ID, which is generated after creating an event subscription successfully." + webhook: NotRequired[WebhookProto] + "The webhook's ID, which is generated after creating a webhook successfully." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class AgentStatusEventSubscriptionCollection(TypedDict): + """Collection of agent status event subscriptions.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[AgentStatusEventSubscriptionProto]] + 'A list of SMS event subscriptions.' + + +class CreateAgentStatusEventSubscription(TypedDict): + """TypedDict representation of the CreateAgentStatusEventSubscription schema.""" + + agent_type: Literal['callcenter'] + 'The agent type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the this agent status event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + + +class UpdateAgentStatusEventSubscription(TypedDict): + """TypedDict representation of the UpdateAgentStatusEventSubscription schema.""" + + agent_type: NotRequired[Literal['callcenter']] + 'The agent type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the this agent status event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here." diff --git a/src/dialpad/schemas/app/__init__.py b/src/dialpad/schemas/app/__init__.py new file mode 100644 index 0000000..b29ae4b --- /dev/null +++ b/src/dialpad/schemas/app/__init__.py @@ -0,0 +1 @@ +# This is an auto-generated schema package. Please do not edit it directly. diff --git a/src/dialpad/schemas/app/setting.py b/src/dialpad/schemas/app/setting.py new file mode 100644 index 0000000..e1bd05f --- /dev/null +++ b/src/dialpad/schemas/app/setting.py @@ -0,0 +1,12 @@ +from typing_extensions import NotRequired, TypedDict + + +class AppSettingProto(TypedDict): + """App settings object.""" + + enabled: NotRequired[bool] + 'Whether or not the OAuth app is enabled for the target.' + is_preferred_service: NotRequired[bool] + 'Whether or not Oauth app is preferred service for screen pop.' + settings: NotRequired[dict] + 'A dynamic object that maps settings to their values.\n\nIt includes all standard settings, i.e. call_logging_enabled, call_recording_logging_enabled,\nvoicemail_logging_enabled and sms_logging_enabled, and any custom settings this OAuth app supports.' diff --git a/src/dialpad/schemas/blocked_number.py b/src/dialpad/schemas/blocked_number.py new file mode 100644 index 0000000..d216d08 --- /dev/null +++ b/src/dialpad/schemas/blocked_number.py @@ -0,0 +1,31 @@ +from typing_extensions import NotRequired, TypedDict + + +class AddBlockedNumbersProto(TypedDict): + """TypedDict representation of the AddBlockedNumbersProto schema.""" + + numbers: NotRequired[list[str]] + 'A list of E164 formatted numbers.' + + +class BlockedNumber(TypedDict): + """Blocked number.""" + + number: NotRequired[str] + 'A phone number (e164 format).' + + +class BlockedNumberCollection(TypedDict): + """Collection of blocked numbers.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[BlockedNumber]] + 'A list of blocked numbers.' + + +class RemoveBlockedNumbersProto(TypedDict): + """TypedDict representation of the RemoveBlockedNumbersProto schema.""" + + numbers: NotRequired[list[str]] + 'A list of E164 formatted numbers.' diff --git a/src/dialpad/schemas/breadcrumbs.py b/src/dialpad/schemas/breadcrumbs.py new file mode 100644 index 0000000..f0ad4ab --- /dev/null +++ b/src/dialpad/schemas/breadcrumbs.py @@ -0,0 +1,22 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class ApiCallRouterBreadcrumb(TypedDict): + """Call routing breadcrumb.""" + + breadcrumb_type: NotRequired[Literal['callrouter', 'external_api']] + 'Breadcrumb type' + date: NotRequired[int] + 'Date when this breadcrumb was added' + request: NotRequired[dict] + 'The HTTP request payload associated with this breadcrumb' + response: NotRequired[dict] + 'The HTTP response associated with this breadcrumb' + target_id: NotRequired[int] + 'The target id' + target_type: NotRequired[str] + 'The target type from call' + url: NotRequired[str] + 'The URL that should be used to drive call routing decisions.' diff --git a/src/dialpad/schemas/call.py b/src/dialpad/schemas/call.py new file mode 100644 index 0000000..2ada5f9 --- /dev/null +++ b/src/dialpad/schemas/call.py @@ -0,0 +1,368 @@ +from typing import Literal, Union + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.breadcrumbs import ApiCallRouterBreadcrumb +from dialpad.schemas.userdevice import UserDeviceProto + + +class ActiveCallProto(TypedDict): + """Active call.""" + + call_state: NotRequired[str] + 'The current state of the call.' + id: NotRequired[int] + 'A unique number ID automatically assigned to each call.' + is_recording: NotRequired[bool] + 'A boolean indicating whether the call is currently being recorded.' + + +class AddCallLabelsMessage(TypedDict): + """Create labels for a call""" + + labels: NotRequired[list[str]] + 'The list of labels to attach to the call' + + +class NumberTransferDestination(TypedDict): + """TypedDict representation of the NumberTransferDestination schema.""" + + number: str + 'The phone number which the call should be transferred to.' + + +class TargetTransferDestination(TypedDict): + """TypedDict representation of the TargetTransferDestination schema.""" + + target_id: int + 'The ID of the target that will be used to transfer the call.' + target_type: Literal['callcenter', 'department', 'office', 'user'] + 'Type of target that will be used to transfer the call.' + + +class AddParticipantMessage(TypedDict): + """Add participant into a Call.""" + + participant: Union[NumberTransferDestination, TargetTransferDestination] + 'New member of the call to add. Can be a number or a Target. In case of a target, it must have a primary number assigned.' + + +class CallContactProto(TypedDict): + """Call contact.""" + + email: NotRequired[str] + 'The primary email address of the contact.' + id: NotRequired[str] + 'A unique number ID for the contact.' + name: NotRequired[str] + '[single-line only]\n\nName of contact.' + phone: NotRequired[str] + 'The primary phone number of the contact.' + type: NotRequired[str] + 'Type of the contact.' + + +class CallRecordingDetailsProto(TypedDict): + """Call recording details.""" + + duration: NotRequired[int] + 'The duration of the recording in milliseconds' + id: NotRequired[str] + 'The recording ID' + recording_type: NotRequired[Literal['admincallrecording', 'callrecording', 'voicemail']] + 'The recording type' + start_time: NotRequired[int] + 'The recording start timestamp' + url: NotRequired[str] + 'The access URL of the recording' + + +class CallProto(TypedDict): + """Call.""" + + admin_call_recording_share_links: NotRequired[list[str]] + 'A list of admin call recording share links.' + call_id: NotRequired[int] + 'A unique number ID automatically assigned to each call.' + call_recording_share_links: NotRequired[list[str]] + 'A list of call recording share links.' + contact: NotRequired[CallContactProto] + 'This is the contact involved in the call.' + csat_recording_urls: NotRequired[list[str]] + 'A list of CSAT urls related to the call.' + csat_score: NotRequired[str] + 'CSAT score related to the call.' + csat_transcriptions: NotRequired[list[str]] + 'A list of CSAT texts related to the call.' + custom_data: NotRequired[str] + 'Any custom data.' + date_connected: NotRequired[int] + 'Timestamp when Dialpad connected the call.' + date_ended: NotRequired[int] + 'Timestamp when the call was hung up.' + date_rang: NotRequired[int] + 'Timestamp when Dialpad first detects an inbound call toa mainline, department, or person.' + date_started: NotRequired[int] + 'Timestamp when the call began in the Dialpad system before being connected.' + direction: NotRequired[str] + 'Call direction. Indicates whether a call was outbound or inbound.' + duration: NotRequired[float] + 'Duration of the call in milliseconds.' + entry_point_call_id: NotRequired[int] + 'Call ID of the associated entry point call.' + entry_point_target: NotRequired[CallContactProto] + 'Where a call initially dialed for inbound calls to Dialpad.' + event_timestamp: NotRequired[int] + 'Timestamp of when this call event happened.' + external_number: NotRequired[str] + 'The phone number external to your organization.' + group_id: NotRequired[str] + 'Unique ID of the department, mainline, or call queue associated with the call.' + internal_number: NotRequired[str] + 'The phone number internal to your organization.' + is_transferred: NotRequired[bool] + 'Boolean indicating whether or not the call was transferred.' + labels: NotRequired[list[str]] + "The label's associated to this call." + master_call_id: NotRequired[int] + 'The master id of the specified call.' + mos_score: NotRequired[float] + 'Mean Opinion Score' + operator_call_id: NotRequired[int] + 'The id of operator.' + proxy_target: NotRequired[CallContactProto] + 'Caller ID used by the Dialpad user for outbound calls.' + recording_details: NotRequired[list[CallRecordingDetailsProto]] + 'List of associated recording details.' + routing_breadcrumbs: NotRequired[list[ApiCallRouterBreadcrumb]] + 'The routing breadcrumbs' + screen_recording_urls: NotRequired[list[str]] + 'A list of screen recording urls.' + state: NotRequired[str] + 'Current call state.' + target: NotRequired[CallContactProto] + 'This is the target that the Dialpad user dials or receives a call from.' + total_duration: NotRequired[float] + 'Duration of the call in milliseconds, including ring time.' + transcription_text: NotRequired[str] + 'Text of call transcription.' + voicemail_share_link: NotRequired[str] + 'Share link to the voicemail recording.' + was_recorded: NotRequired[bool] + 'Boolean indicating whether or not the call was recorded.' + + +class CallCollection(TypedDict): + """Collection of calls.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[CallProto]] + 'A list of calls.' + + +class CallTransferDestination(TypedDict): + """TypedDict representation of the CallTransferDestination schema.""" + + call_id: int + 'The id of the ongoing call which the call should be transferred to.' + + +class CallbackMessage(TypedDict): + """TypedDict representation of the CallbackMessage schema.""" + + call_center_id: NotRequired[int] + 'The ID of a call center that will be used to fulfill the callback.' + phone_number: NotRequired[str] + 'The e164-formatted number to call back' + + +class CallbackProto(TypedDict): + """Note: Position indicates the new callback request's position in the queue, with 1 being at the front.""" + + position: NotRequired[int] + "Indicates the new callback request's position in the queue, with 1 being at the front." + + +class InitiateCallMessage(TypedDict): + """TypedDict representation of the InitiateCallMessage schema.""" + + custom_data: NotRequired[str] + 'Extra data to associate with the call. This will be passed through to any subscribed call events.' + group_id: NotRequired[int] + 'The ID of a group that will be used to initiate the call.' + group_type: NotRequired[Literal['callcenter', 'department', 'office']] + 'The type of a group that will be used to initiate the call.' + outbound_caller_id: NotRequired[str] + 'The e164-formatted number shown to the call recipient (or "blocked").\n\nIf set to "blocked", the recipient will receive a call from "unknown caller". The number can be the caller\'s number, or the caller\'s group number if the group is provided,\nor the caller\'s company reserved number.' + phone_number: NotRequired[str] + 'The e164-formatted number to call.' + + +class InitiatedCallProto(TypedDict): + """Initiated call.""" + + device: NotRequired[UserDeviceProto] + 'The device used to initiate the call.' + + +class InitiatedIVRCallProto(TypedDict): + """Initiated IVR call.""" + + call_id: int + 'The ID of the initiated call.' + + +class OutboundIVRMessage(TypedDict): + """TypedDict representation of the OutboundIVRMessage schema.""" + + custom_data: NotRequired[str] + 'Extra data to associate with the call. This will be passed through to any subscribed call events.' + outbound_caller_id: NotRequired[str] + 'The e164-formatted number shown to the call recipient (or "blocked").' + phone_number: str + 'The e164-formatted number to call.' + target_id: int + 'The ID of a group that will be used to initiate the call.' + target_type: Literal['callcenter', 'department', 'office'] + 'The type of a group that will be used to initiate the call.' + + +class RingCallMessage(TypedDict): + """TypedDict representation of the RingCallMessage schema.""" + + custom_data: NotRequired[str] + 'Extra data to associate with the call. This will be passed through to any subscribed call events.' + device_id: NotRequired[str] + "The device's id." + group_id: NotRequired[int] + 'The ID of a group that will be used to initiate the call.' + group_type: NotRequired[Literal['callcenter', 'department', 'office']] + 'The type of a group that will be used to initiate the call.' + is_consult: NotRequired[bool] + 'Enables the creation of a second call. If there is an ongoing call, it puts it on hold.' + outbound_caller_id: NotRequired[str] + 'The e164-formatted number shown to the call recipient (or "blocked").\n\nIf set to "blocked", the recipient will receive a call from "unknown caller". The number can be the caller\'s number, or the caller\'s group number if the group is provided, or the caller\'s company reserved number.' + phone_number: str + 'The e164-formatted number to call.' + user_id: int + 'The id of the user who should make the outbound call.' + + +class RingCallProto(TypedDict): + """Ringing call.""" + + call_id: NotRequired[int] + 'The ID of the created call.' + + +class ToggleViMessage(TypedDict): + """TypedDict representation of the ToggleViMessage schema.""" + + enable_vi: NotRequired[bool] + 'Whether or not call vi should be enabled.' + vi_locale: NotRequired[ + Literal[ + 'en-au', + 'en-ca', + 'en-de', + 'en-fr', + 'en-gb', + 'en-it', + 'en-jp', + 'en-mx', + 'en-nl', + 'en-nz', + 'en-pt', + 'en-us', + 'es-au', + 'es-ca', + 'es-de', + 'es-es', + 'es-fr', + 'es-gb', + 'es-it', + 'es-jp', + 'es-mx', + 'es-nl', + 'es-nz', + 'es-pt', + 'es-us', + 'fr-au', + 'fr-ca', + 'fr-de', + 'fr-es', + 'fr-fr', + 'fr-gb', + 'fr-it', + 'fr-jp', + 'fr-mx', + 'fr-nl', + 'fr-nz', + 'fr-pt', + 'fr-us', + ] + ] + 'The locale to use for vi.' + + +class ToggleViProto(TypedDict): + """VI state.""" + + call_state: NotRequired[str] + 'Current call state.' + enable_vi: NotRequired[bool] + 'Whether vi is enabled.' + id: NotRequired[int] + 'The id of the toggled call.' + vi_locale: NotRequired[str] + 'The locale used for vi.' + + +class TransferCallMessage(TypedDict): + """TypedDict representation of the TransferCallMessage schema.""" + + custom_data: NotRequired[str] + 'Extra data to associate with the call. This will be passed through to any subscribed call events.' + to: NotRequired[ + Union[CallTransferDestination, NumberTransferDestination, TargetTransferDestination] + ] + 'Destination of the call that will be transfer. It can be a single option between a number, \nan existing call or a target' + transfer_state: NotRequired[Literal['hold', 'parked', 'preanswer', 'voicemail']] + "The state which the call should take when it's transferred to." + + +class TransferredCallProto(TypedDict): + """Transferred call.""" + + call_id: NotRequired[int] + "The call's id." + transferred_to_number: NotRequired[str] + 'The phone number which the call has been transferred to.' + transferred_to_state: NotRequired[Literal['hold', 'parked', 'preanswer', 'voicemail']] + 'The state which the call has been transferred to.' + + +class UnparkCallMessage(TypedDict): + """TypedDict representation of the UnparkCallMessage schema.""" + + user_id: int + 'The id of the user who should unpark the call.' + + +class UpdateActiveCallMessage(TypedDict): + """TypedDict representation of the UpdateActiveCallMessage schema.""" + + is_recording: NotRequired[bool] + 'Whether or not recording should be enabled.' + play_message: NotRequired[bool] + 'Whether or not to play a message to indicate the call is being recorded (or recording has stopped).' + recording_type: NotRequired[Literal['all', 'group', 'user']] + 'Whether or not to toggle recording for the operator call (personal recording),\nthe group call (department recording), or both.\n\nOnly applicable for group calls (call centers, departments, etc.)' + + +class ValidateCallbackProto(TypedDict): + """Callback (validation).""" + + success: NotRequired[bool] + 'Whether the callback request would have been queued successfully.' diff --git a/src/dialpad/schemas/call_event_subscription.py b/src/dialpad/schemas/call_event_subscription.py new file mode 100644 index 0000000..febe578 --- /dev/null +++ b/src/dialpad/schemas/call_event_subscription.py @@ -0,0 +1,223 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class CallEventSubscriptionProto(TypedDict): + """Call event subscription.""" + + call_states: NotRequired[ + list[ + Literal[ + 'admin', + 'admin_recording', + 'ai_playbook', + 'all', + 'barge', + 'blocked', + 'call_transcription', + 'calling', + 'connected', + 'csat', + 'dispositions', + 'eavesdrop', + 'hangup', + 'hold', + 'merged', + 'missed', + 'monitor', + 'parked', + 'pcsat', + 'postcall', + 'preanswer', + 'queued', + 'recap_action_items', + 'recap_outcome', + 'recap_purposes', + 'recap_summary', + 'recording', + 'ringing', + 'takeover', + 'transcription', + 'voicemail', + 'voicemail_uploaded', + ] + ] + ] + "The call event subscription's list of call states." + enabled: NotRequired[bool] + 'Whether or not the call event subscription is enabled.' + group_calls_only: NotRequired[bool] + 'Call event subscription for group calls only.' + id: NotRequired[int] + "The event subscription's ID, which is generated after creating an event subscription successfully." + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The target type.' + webhook: NotRequired[WebhookProto] + "The webhook that's associated with this event subscription." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class CallEventSubscriptionCollection(TypedDict): + """Collection of call event subscriptions.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[CallEventSubscriptionProto]] + 'A list of call event subscriptions.' + + +class CreateCallEventSubscription(TypedDict): + """TypedDict representation of the CreateCallEventSubscription schema.""" + + call_states: NotRequired[ + list[ + Literal[ + 'admin', + 'admin_recording', + 'ai_playbook', + 'all', + 'barge', + 'blocked', + 'call_transcription', + 'calling', + 'connected', + 'csat', + 'dispositions', + 'eavesdrop', + 'hangup', + 'hold', + 'merged', + 'missed', + 'monitor', + 'parked', + 'pcsat', + 'postcall', + 'preanswer', + 'queued', + 'recap_action_items', + 'recap_outcome', + 'recap_purposes', + 'recap_summary', + 'recording', + 'ringing', + 'takeover', + 'transcription', + 'voicemail', + 'voicemail_uploaded', + ] + ] + ] + "The call event subscription's list of call states." + enabled: NotRequired[bool] + 'Whether or not the call event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + group_calls_only: NotRequired[bool] + 'Call event subscription for group calls only.' + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The target type.' + + +class UpdateCallEventSubscription(TypedDict): + """TypedDict representation of the UpdateCallEventSubscription schema.""" + + call_states: NotRequired[ + list[ + Literal[ + 'admin', + 'admin_recording', + 'ai_playbook', + 'all', + 'barge', + 'blocked', + 'call_transcription', + 'calling', + 'connected', + 'csat', + 'dispositions', + 'eavesdrop', + 'hangup', + 'hold', + 'merged', + 'missed', + 'monitor', + 'parked', + 'pcsat', + 'postcall', + 'preanswer', + 'queued', + 'recap_action_items', + 'recap_outcome', + 'recap_purposes', + 'recap_summary', + 'recording', + 'ringing', + 'takeover', + 'transcription', + 'voicemail', + 'voicemail_uploaded', + ] + ] + ] + "The call event subscription's list of call states." + enabled: NotRequired[bool] + 'Whether or not the call event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here." + group_calls_only: NotRequired[bool] + 'Call event subscription for group calls only.' + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The target type.' diff --git a/src/dialpad/schemas/call_label.py b/src/dialpad/schemas/call_label.py new file mode 100644 index 0000000..309c399 --- /dev/null +++ b/src/dialpad/schemas/call_label.py @@ -0,0 +1,8 @@ +from typing_extensions import NotRequired, TypedDict + + +class CompanyCallLabels(TypedDict): + """Company Labels.""" + + labels: NotRequired[list[str]] + 'The labels associated to this company.' diff --git a/src/dialpad/schemas/call_review_share_link.py b/src/dialpad/schemas/call_review_share_link.py new file mode 100644 index 0000000..1c9bd03 --- /dev/null +++ b/src/dialpad/schemas/call_review_share_link.py @@ -0,0 +1,32 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CallReviewShareLink(TypedDict): + """Reponse for the call review share link.""" + + access_link: NotRequired[str] + 'The access link where the call review can be listened or downloaded.' + call_id: NotRequired[int] + "The call's id." + id: NotRequired[str] + "The call review share link's ID." + privacy: NotRequired[Literal['company', 'public']] + 'The privacy state of the call review sharel link.' + + +class CreateCallReviewShareLink(TypedDict): + """Input for POST request to create a call review share link.""" + + call_id: NotRequired[int] + "The call's id." + privacy: NotRequired[Literal['company', 'public']] + "The privacy state of the recording share link, 'company' will be set as default." + + +class UpdateCallReviewShareLink(TypedDict): + """Input for PUT request to update a call review share link.""" + + privacy: Literal['company', 'public'] + 'The privacy state of the recording share link' diff --git a/src/dialpad/schemas/call_router.py b/src/dialpad/schemas/call_router.py new file mode 100644 index 0000000..a0f20fa --- /dev/null +++ b/src/dialpad/schemas/call_router.py @@ -0,0 +1,117 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.signature import SignatureProto + + +class ApiCallRouterProto(TypedDict): + """API call router.""" + + default_target_id: NotRequired[int] + 'The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.' + default_target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The entity type of the default target.' + enabled: NotRequired[bool] + 'If set to False, the call router will skip the routing url and instead forward calls straight to the default target.' + id: NotRequired[int] + "The API call router's ID." + name: NotRequired[str] + '[single-line only]\n\nA human-readable display name for the router.' + office_id: NotRequired[int] + 'The ID of the office to which this router belongs.' + phone_numbers: NotRequired[list[str]] + 'The phone numbers that will cause inbound calls to hit this call router.' + routing_url: NotRequired[str] + 'The URL that should be used to drive call routing decisions.' + signature: NotRequired[SignatureProto] + 'The signature that will be used to sign JWTs for routing requests.' + + +class ApiCallRouterCollection(TypedDict): + """Collection of API call routers.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[ApiCallRouterProto]] + 'A list of call routers.' + + +class CreateApiCallRouterMessage(TypedDict): + """TypedDict representation of the CreateApiCallRouterMessage schema.""" + + default_target_id: int + 'The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.' + default_target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + 'The entity type of the default target.' + enabled: NotRequired[bool] + 'If set to False, the call router will skip the routing url and instead forward calls straight to the default target.' + name: str + '[single-line only]\n\nA human-readable display name for the router.' + office_id: int + 'The ID of the office to which this router belongs.' + routing_url: str + 'The URL that should be used to drive call routing decisions.' + secret: NotRequired[str] + "[single-line only]\n\nThe call router's signature secret. This is a plain text string that you should generate with a minimum length of 32 characters." + + +class UpdateApiCallRouterMessage(TypedDict): + """TypedDict representation of the UpdateApiCallRouterMessage schema.""" + + default_target_id: NotRequired[int] + 'The ID of the target that should be used as a fallback destination for calls if the call router is disabled or fails.' + default_target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The entity type of the default target.' + enabled: NotRequired[bool] + 'If set to False, the call router will skip the routing url and instead forward calls straight to the default target.' + name: NotRequired[str] + '[single-line only]\n\nA human-readable display name for the router.' + office_id: NotRequired[int] + 'The ID of the office to which this router belongs.' + reset_error_count: NotRequired[bool] + 'Sets the auto-disablement routing error count back to zero.\n\nCall routers maintain a count of the number of errors that have occured within the past hour, and automatically become disabled when that count exceeds 10.\n\nSetting enabled to true via the API will not reset that count, which means that the router will likely become disabled again after one more error. In most cases, this will be useful for testing fixes in your routing API, but in some circumstances it may be desirable to reset that counter.' + routing_url: NotRequired[str] + 'The URL that should be used to drive call routing decisions.' + secret: NotRequired[str] + "[single-line only]\n\nThe call router's signature secret. This is a plain text string that you should generate with a minimum length of 32 characters." diff --git a/src/dialpad/schemas/caller_id.py b/src/dialpad/schemas/caller_id.py new file mode 100644 index 0000000..841fe34 --- /dev/null +++ b/src/dialpad/schemas/caller_id.py @@ -0,0 +1,36 @@ +from typing_extensions import NotRequired, TypedDict + + +class GroupProto(TypedDict): + """Group caller ID.""" + + caller_id: NotRequired[str] + 'A caller id from the operator group. (e164-formatted)' + display_name: NotRequired[str] + '[single-line only]\n\nThe operator group display name' + + +class CallerIdProto(TypedDict): + """Caller ID.""" + + caller_id: NotRequired[str] + 'The caller id number for the user' + forwarding_numbers: NotRequired[list[str]] + "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call." + groups: NotRequired[list[GroupProto]] + 'The groups from the user' + id: int + 'The ID of the user.' + office_main_line: NotRequired[str] + 'The office main line number' + phone_numbers: NotRequired[list[str]] + 'A list of phone numbers belonging to this user.' + primary_phone: NotRequired[str] + 'The user primary phone number' + + +class SetCallerIdMessage(TypedDict): + """TypedDict representation of the SetCallerIdMessage schema.""" + + caller_id: str + "Phone number (e164 formatted) that will be defined as a Caller ID for the target. Use 'blocked' to block the Caller ID." diff --git a/src/dialpad/schemas/change_log_event_subscription.py b/src/dialpad/schemas/change_log_event_subscription.py new file mode 100644 index 0000000..9ba2bca --- /dev/null +++ b/src/dialpad/schemas/change_log_event_subscription.py @@ -0,0 +1,44 @@ +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class ChangeLogEventSubscriptionProto(TypedDict): + """Change log event subscription.""" + + enabled: NotRequired[bool] + 'Whether or not the change log event subscription is enabled.' + id: NotRequired[int] + "The event subscription's ID, which is generated after creating an event subscription successfully." + webhook: NotRequired[WebhookProto] + "The webhook's ID, which is generated after creating a webhook successfully." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class ChangeLogEventSubscriptionCollection(TypedDict): + """Collection of change log event subscriptions.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[ChangeLogEventSubscriptionProto]] + 'A list of change log event subscriptions.' + + +class CreateChangeLogEventSubscription(TypedDict): + """TypedDict representation of the CreateChangeLogEventSubscription schema.""" + + enabled: NotRequired[bool] + 'Whether or not the this change log event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + + +class UpdateChangeLogEventSubscription(TypedDict): + """TypedDict representation of the UpdateChangeLogEventSubscription schema.""" + + enabled: NotRequired[bool] + 'Whether or not the change log event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here." diff --git a/src/dialpad/schemas/channel.py b/src/dialpad/schemas/channel.py new file mode 100644 index 0000000..f5d7008 --- /dev/null +++ b/src/dialpad/schemas/channel.py @@ -0,0 +1,34 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class ChannelProto(TypedDict): + """Channel.""" + + id: NotRequired[int] + 'The channel id.' + name: str + '[single-line only]\n\nThe channel name.' + + +class ChannelCollection(TypedDict): + """Collection of channels.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[ChannelProto]] + 'A list of channels.' + + +class CreateChannelMessage(TypedDict): + """TypedDict representation of the CreateChannelMessage schema.""" + + description: str + 'The description of the channel.' + name: str + '[single-line only]\n\nThe name of the channel.' + privacy_type: Literal['private', 'public'] + 'The privacy type of the channel.' + user_id: NotRequired[int] + 'The ID of the user who owns the channel. Just for company level API key.' diff --git a/src/dialpad/schemas/coaching_team.py b/src/dialpad/schemas/coaching_team.py new file mode 100644 index 0000000..6056433 --- /dev/null +++ b/src/dialpad/schemas/coaching_team.py @@ -0,0 +1,131 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CoachingTeamProto(TypedDict): + """Coaching team.""" + + allow_trainee_eavesdrop: NotRequired[bool] + 'The boolean to tell if trainees are allowed to eavesdrop.' + company_id: NotRequired[int] + "The company's id." + country: NotRequired[str] + 'The country in which the coaching team is situated.' + id: NotRequired[int] + 'Id of the coaching team.' + name: NotRequired[str] + '[single-line only]\n\nName of the coaching team.' + office_id: NotRequired[int] + "The office's id." + phone_numbers: NotRequired[list[str]] + 'The phone number(s) assigned to this coaching team.' + ring_seconds: NotRequired[int] + 'The number of seconds to ring the main line before going to voicemail.\n\n(or an other-wise-specified no_operators_action).' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The enablement state of the team.' + team_description: NotRequired[str] + 'Description of the coaching team.' + + +class CoachingTeamCollection(TypedDict): + """Collection of coaching team.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[CoachingTeamProto]] + 'A list of coaching teams.' + + +class CoachingTeamMemberProto(TypedDict): + """Coaching team member.""" + + admin_office_ids: NotRequired[list[int]] + 'The list of ids of offices where the user is an admin.' + company_id: NotRequired[int] + "The id of user's company." + country: NotRequired[str] + 'Country of the user.' + date_active: NotRequired[str] + 'The date when the user is activated.' + date_added: NotRequired[str] + 'The date when the user is added.' + date_first_login: NotRequired[str] + 'The date when the user is logged in first time.' + do_not_disturb: NotRequired[bool] + 'Boolean to tell if the user is on DND.' + emails: NotRequired[list[str]] + 'Emails of the user.' + extension: NotRequired[str] + 'Extension of the user.' + first_name: NotRequired[str] + '[single-line only]\n\nFirst name of the user.' + forwarding_numbers: NotRequired[list[str]] + 'The list of forwarding numbers set for the user.' + id: int + 'Unique id of the user.' + image_url: NotRequired[str] + "Link to the user's profile image." + is_admin: NotRequired[bool] + 'Boolean to tell if the user is admin.' + is_available: NotRequired[bool] + 'Boolean to tell if the user is available.' + is_on_duty: NotRequired[bool] + 'Boolean to tell if the user is on duty.' + is_online: NotRequired[bool] + 'Boolean to tell if the user is online.' + is_super_admin: NotRequired[bool] + 'Boolean to tell if the user is super admin.' + job_title: NotRequired[str] + '[single-line only]\n\nJob title of the user.' + language: NotRequired[str] + 'Language of the user.' + last_name: NotRequired[str] + '[single-line only]\n\nLast name of the User.' + license: NotRequired[ + Literal[ + 'admins', + 'agents', + 'dpde_all', + 'dpde_one', + 'lite_lines', + 'lite_support_agents', + 'magenta_lines', + 'talk', + ] + ] + 'License of the user.' + location: NotRequired[str] + '[single-line only]\n\nThe self-reported location of the user.' + muted: NotRequired[bool] + 'Boolean to tell if the user is muted.' + office_id: NotRequired[int] + "Id of the user's office." + phone_numbers: NotRequired[list[str]] + 'The list of phone numbers assigned to the user.' + role: Literal['coach', 'trainee'] + 'The role of the user within the coaching team.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The enablement state of the user.' + status_message: NotRequired[str] + '[single-line only]\n\nStatus message set by the user.' + timezone: NotRequired[str] + 'Timezone of the user.' + + +class CoachingTeamMemberCollection(TypedDict): + """Collection of coaching team members.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[CoachingTeamMemberProto]] + 'A list of team members.' + + +class CoachingTeamMemberMessage(TypedDict): + """Coaching team membership.""" + + member_id: str + 'The id of the user added to the coaching team.' + role: Literal['coach', 'trainee'] + 'The role of the user added.' diff --git a/src/dialpad/schemas/company.py b/src/dialpad/schemas/company.py new file mode 100644 index 0000000..23444d3 --- /dev/null +++ b/src/dialpad/schemas/company.py @@ -0,0 +1,24 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CompanyProto(TypedDict): + """Company.""" + + account_type: NotRequired[Literal['enterprise', 'free', 'pro', 'standard']] + 'Company pricing tier.' + admin_email: NotRequired[str] + 'Email address of the company administrator.' + country: NotRequired[str] + 'Primary country of the company.' + domain: NotRequired[str] + '[single-line only]\n\nDomain name of user emails.' + id: NotRequired[int] + "The company's id." + name: NotRequired[str] + '[single-line only]\n\nThe name of the company.' + office_count: NotRequired[int] + 'The number of offices belonging to this company' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'Enablement state of the company.' diff --git a/src/dialpad/schemas/contact.py b/src/dialpad/schemas/contact.py new file mode 100644 index 0000000..3920a2f --- /dev/null +++ b/src/dialpad/schemas/contact.py @@ -0,0 +1,120 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class ContactProto(TypedDict): + """Contact.""" + + company_name: NotRequired[str] + '[single-line only]\n\nThe name of the company that this contact is employed by.' + display_name: NotRequired[str] + '[single-line only]\n\nThe formatted name that will be displayed for this contact.' + emails: NotRequired[list[str]] + 'The email addresses associated with this contact.' + extension: NotRequired[str] + "The contact's extension number." + first_name: NotRequired[str] + '[single-line only]\n\nThe given name of the contact.' + id: NotRequired[str] + 'The ID of the contact.' + job_title: NotRequired[str] + '[single-line only]\n\nThe job title of this contact.' + last_name: NotRequired[str] + '[single-line only]\n\nThe family name of the contact.' + owner_id: NotRequired[str] + 'The ID of the entity that owns this contact.' + phones: NotRequired[list[str]] + 'The phone numbers associated with this contact.' + primary_email: NotRequired[str] + 'The email address to display in a context where only one email can be shown.' + primary_phone: NotRequired[str] + 'The primary phone number to be used when calling this contact.' + trunk_group: NotRequired[str] + '[Deprecated]' + type: NotRequired[Literal['local', 'shared']] + 'Either shared or local.' + urls: NotRequired[list[str]] + 'A list of websites associated with or belonging to this contact.' + + +class ContactCollection(TypedDict): + """Collection of contacts.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[ContactProto]] + 'A list of contact objects.' + + +class CreateContactMessage(TypedDict): + """TypedDict representation of the CreateContactMessage schema.""" + + company_name: NotRequired[str] + "[single-line only]\n\nThe contact's company name." + emails: NotRequired[list[str]] + "The contact's emails.\n\nThe first email in the list is the contact's primary email." + extension: NotRequired[str] + "The contact's extension number." + first_name: str + "[single-line only]\n\nThe contact's first name." + job_title: NotRequired[str] + "[single-line only]\n\nThe contact's job title." + last_name: str + "[single-line only]\n\nThe contact's last name." + owner_id: NotRequired[str] + 'The id of the user who will own this contact.\n\nIf provided, a local contact will be created for this user. Otherwise, the contact will be created as a shared contact in your company.' + phones: NotRequired[list[str]] + "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone." + trunk_group: NotRequired[str] + '[Deprecated]' + urls: NotRequired[list[str]] + 'A list of websites associated with or belonging to this contact.' + + +class CreateContactMessageWithUid(TypedDict): + """TypedDict representation of the CreateContactMessageWithUid schema.""" + + company_name: NotRequired[str] + "[single-line only]\n\nThe contact's company name." + emails: NotRequired[list[str]] + "The contact's emails.\n\nThe first email in the list is the contact's primary email." + extension: NotRequired[str] + "The contact's extension number." + first_name: str + "[single-line only]\n\nThe contact's first name." + job_title: NotRequired[str] + "[single-line only]\n\nThe contact's job title." + last_name: str + "[single-line only]\n\nThe contact's last name." + phones: NotRequired[list[str]] + "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone." + trunk_group: NotRequired[str] + '[Deprecated]' + uid: str + "The unique id to be included as part of the contact's generated id." + urls: NotRequired[list[str]] + 'A list of websites associated with or belonging to this contact.' + + +class UpdateContactMessage(TypedDict): + """TypedDict representation of the UpdateContactMessage schema.""" + + company_name: NotRequired[str] + "[single-line only]\n\nThe contact's company name." + emails: NotRequired[list[str]] + "The contact's emails.\n\nThe first email in the list is the contact's primary email." + extension: NotRequired[str] + "The contact's extension number." + first_name: NotRequired[str] + "[single-line only]\n\nThe contact's first name." + job_title: NotRequired[str] + "[single-line only]\n\nThe contact's job title." + last_name: NotRequired[str] + "[single-line only]\n\nThe contact's last name." + phones: NotRequired[list[str]] + "The contact's phone numbers.\n\nThe phone number must be in e164 format. The first number in the list is the contact's primary phone." + trunk_group: NotRequired[str] + '[Deprecated]' + urls: NotRequired[list[str]] + 'A list of websites associated with or belonging to this contact.' diff --git a/src/dialpad/schemas/contact_event_subscription.py b/src/dialpad/schemas/contact_event_subscription.py new file mode 100644 index 0000000..b31223d --- /dev/null +++ b/src/dialpad/schemas/contact_event_subscription.py @@ -0,0 +1,52 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class ContactEventSubscriptionProto(TypedDict): + """Contact event subscription.""" + + contact_type: NotRequired[Literal['local', 'shared']] + 'The contact type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the contact event subscription is enabled.' + id: NotRequired[int] + 'The ID of the contact event subscription object.' + webhook: NotRequired[WebhookProto] + "The webhook's ID, which is generated after creating a webhook successfully." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class ContactEventSubscriptionCollection(TypedDict): + """Collection of contact event subscriptions.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[ContactEventSubscriptionProto]] + 'A list event subscriptions.' + + +class CreateContactEventSubscription(TypedDict): + """TypedDict representation of the CreateContactEventSubscription schema.""" + + contact_type: Literal['local', 'shared'] + 'The contact type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the contact event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + + +class UpdateContactEventSubscription(TypedDict): + """TypedDict representation of the UpdateContactEventSubscription schema.""" + + contact_type: Literal['local', 'shared'] + 'The contact type this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the contact event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here." diff --git a/src/dialpad/schemas/custom_ivr.py b/src/dialpad/schemas/custom_ivr.py new file mode 100644 index 0000000..1c11668 --- /dev/null +++ b/src/dialpad/schemas/custom_ivr.py @@ -0,0 +1,211 @@ +from typing import Annotated, Literal + +from typing_extensions import NotRequired, TypedDict + + +class CreateCustomIvrMessage(TypedDict): + """TypedDict representation of the CreateCustomIvrMessage schema.""" + + description: NotRequired[str] + '[single-line only]\n\nThe description of the new IVR. Max 256 characters.' + file: Annotated[str, 'base64'] + 'An MP3 audio file. The file needs to be Base64-encoded.' + ivr_type: Literal[ + 'ASK_FIRST_OPERATOR_NOT_AVAILABLE', + 'AUTO_RECORDING', + 'CALLAI_AUTO_RECORDING', + 'CG_AUTO_RECORDING', + 'CLOSED', + 'CLOSED_DEPARTMENT_INTRO', + 'CLOSED_MENU', + 'CLOSED_MENU_OPTION', + 'CSAT_INTRO', + 'CSAT_OUTRO', + 'CSAT_PREAMBLE', + 'CSAT_QUESTION', + 'DEPARTMENT_INTRO', + 'GREETING', + 'HOLD_AGENT_READY', + 'HOLD_APPREC', + 'HOLD_CALLBACK_ACCEPT', + 'HOLD_CALLBACK_ACCEPTED', + 'HOLD_CALLBACK_CONFIRM', + 'HOLD_CALLBACK_CONFIRM_NUMBER', + 'HOLD_CALLBACK_DIFFERENT_NUMBER', + 'HOLD_CALLBACK_DIRECT', + 'HOLD_CALLBACK_FULFILLED', + 'HOLD_CALLBACK_INVALID_NUMBER', + 'HOLD_CALLBACK_KEYPAD', + 'HOLD_CALLBACK_REJECT', + 'HOLD_CALLBACK_REJECTED', + 'HOLD_CALLBACK_REQUEST', + 'HOLD_CALLBACK_REQUESTED', + 'HOLD_CALLBACK_SAME_NUMBER', + 'HOLD_CALLBACK_TRY_AGAIN', + 'HOLD_CALLBACK_UNDIALABLE', + 'HOLD_ESCAPE_VM_EIGHT', + 'HOLD_ESCAPE_VM_FIVE', + 'HOLD_ESCAPE_VM_FOUR', + 'HOLD_ESCAPE_VM_NINE', + 'HOLD_ESCAPE_VM_ONE', + 'HOLD_ESCAPE_VM_POUND', + 'HOLD_ESCAPE_VM_SEVEN', + 'HOLD_ESCAPE_VM_SIX', + 'HOLD_ESCAPE_VM_STAR', + 'HOLD_ESCAPE_VM_TEN', + 'HOLD_ESCAPE_VM_THREE', + 'HOLD_ESCAPE_VM_TWO', + 'HOLD_ESCAPE_VM_ZERO', + 'HOLD_INTERRUPT', + 'HOLD_INTRO', + 'HOLD_MUSIC', + 'HOLD_POSITION_EIGHT', + 'HOLD_POSITION_FIVE', + 'HOLD_POSITION_FOUR', + 'HOLD_POSITION_MORE', + 'HOLD_POSITION_NINE', + 'HOLD_POSITION_ONE', + 'HOLD_POSITION_SEVEN', + 'HOLD_POSITION_SIX', + 'HOLD_POSITION_TEN', + 'HOLD_POSITION_THREE', + 'HOLD_POSITION_TWO', + 'HOLD_POSITION_ZERO', + 'HOLD_WAIT', + 'MENU', + 'MENU_OPTION', + 'NEXT_TARGET', + 'VM_DROP_MESSAGE', + 'VM_UNAVAILABLE', + 'VM_UNAVAILABLE_CLOSED', + ] + 'Type of IVR.' + name: NotRequired[str] + '[single-line only]\n\nThe name of the new IVR. Max 100 characters.' + target_id: int + 'The ID of the target to which you want to assign this IVR.' + target_type: Literal['callcenter', 'coachingteam', 'department', 'office', 'user'] + 'The type of the target to which you want to assign this IVR.' + + +class CustomIvrDetailsProto(TypedDict): + """Custom IVR details.""" + + date_added: NotRequired[int] + 'Date when this IVR was added.' + description: NotRequired[str] + '[single-line only]\n\nThe description of the IVR.' + id: NotRequired[int] + 'Id of this IVR.' + name: NotRequired[str] + '[single-line only]\n\nThe name of this IVR.' + selected: NotRequired[bool] + 'True if this IVR is selected for this type of IVR.' + text: NotRequired[str] + 'The text for this IVR if there is no mp3.' + + +class CustomIvrProto(TypedDict): + """Custom IVR.""" + + ivr_type: NotRequired[ + Literal[ + 'ASK_FIRST_OPERATOR_NOT_AVAILABLE', + 'AUTO_RECORDING', + 'CALLAI_AUTO_RECORDING', + 'CG_AUTO_RECORDING', + 'CLOSED', + 'CLOSED_DEPARTMENT_INTRO', + 'CLOSED_MENU', + 'CLOSED_MENU_OPTION', + 'CSAT_INTRO', + 'CSAT_OUTRO', + 'CSAT_PREAMBLE', + 'CSAT_QUESTION', + 'DEPARTMENT_INTRO', + 'GREETING', + 'HOLD_AGENT_READY', + 'HOLD_APPREC', + 'HOLD_CALLBACK_ACCEPT', + 'HOLD_CALLBACK_ACCEPTED', + 'HOLD_CALLBACK_CONFIRM', + 'HOLD_CALLBACK_CONFIRM_NUMBER', + 'HOLD_CALLBACK_DIFFERENT_NUMBER', + 'HOLD_CALLBACK_DIRECT', + 'HOLD_CALLBACK_FULFILLED', + 'HOLD_CALLBACK_INVALID_NUMBER', + 'HOLD_CALLBACK_KEYPAD', + 'HOLD_CALLBACK_REJECT', + 'HOLD_CALLBACK_REJECTED', + 'HOLD_CALLBACK_REQUEST', + 'HOLD_CALLBACK_REQUESTED', + 'HOLD_CALLBACK_SAME_NUMBER', + 'HOLD_CALLBACK_TRY_AGAIN', + 'HOLD_CALLBACK_UNDIALABLE', + 'HOLD_ESCAPE_VM_EIGHT', + 'HOLD_ESCAPE_VM_FIVE', + 'HOLD_ESCAPE_VM_FOUR', + 'HOLD_ESCAPE_VM_NINE', + 'HOLD_ESCAPE_VM_ONE', + 'HOLD_ESCAPE_VM_POUND', + 'HOLD_ESCAPE_VM_SEVEN', + 'HOLD_ESCAPE_VM_SIX', + 'HOLD_ESCAPE_VM_STAR', + 'HOLD_ESCAPE_VM_TEN', + 'HOLD_ESCAPE_VM_THREE', + 'HOLD_ESCAPE_VM_TWO', + 'HOLD_ESCAPE_VM_ZERO', + 'HOLD_INTERRUPT', + 'HOLD_INTRO', + 'HOLD_MUSIC', + 'HOLD_POSITION_EIGHT', + 'HOLD_POSITION_FIVE', + 'HOLD_POSITION_FOUR', + 'HOLD_POSITION_MORE', + 'HOLD_POSITION_NINE', + 'HOLD_POSITION_ONE', + 'HOLD_POSITION_SEVEN', + 'HOLD_POSITION_SIX', + 'HOLD_POSITION_TEN', + 'HOLD_POSITION_THREE', + 'HOLD_POSITION_TWO', + 'HOLD_POSITION_ZERO', + 'HOLD_WAIT', + 'MENU', + 'MENU_OPTION', + 'NEXT_TARGET', + 'VM_DROP_MESSAGE', + 'VM_UNAVAILABLE', + 'VM_UNAVAILABLE_CLOSED', + ] + ] + 'Type of IVR.' + ivrs: NotRequired[list[CustomIvrDetailsProto]] + 'A list of IVR detail objects.' + + +class CustomIvrCollection(TypedDict): + """Collection of Custom IVRs.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[CustomIvrProto]] + 'A list of IVRs.' + + +class UpdateCustomIvrDetailsMessage(TypedDict): + """TypedDict representation of the UpdateCustomIvrDetailsMessage schema.""" + + description: NotRequired[str] + '[single-line only]\n\nThe description of the IVR.' + name: NotRequired[str] + '[single-line only]\n\nThe name of this IVR.' + + +class UpdateCustomIvrMessage(TypedDict): + """TypedDict representation of the UpdateCustomIvrMessage schema.""" + + ivr_id: int + 'The id of the ivr that you want to use for the ivr type.' + select_option: NotRequired[Literal['inbound', 'outbound']] + 'For call center auto call recording only. Set ivr for inbound or outbound. Default is both.' diff --git a/src/dialpad/schemas/deskphone.py b/src/dialpad/schemas/deskphone.py new file mode 100644 index 0000000..db2341f --- /dev/null +++ b/src/dialpad/schemas/deskphone.py @@ -0,0 +1,62 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class DeskPhone(TypedDict): + """Desk phone.""" + + byod: NotRequired[bool] + 'Boolean indicating whether this desk phone was purchased through Dialpad.' + device_model: NotRequired[str] + '[single-line only]\n\nThe model name of the device.' + firmware_version: NotRequired[str] + '[single-line only]\n\nThe firmware version currently loaded onto the device.' + id: NotRequired[str] + 'The ID of the desk phone.' + mac_address: NotRequired[str] + '[single-line only]\n\nThe MAC address of the device.' + name: NotRequired[str] + '[single-line only]\n\nA user-prescibed name for this device.' + owner_id: NotRequired[int] + 'The ID of the device owner.' + owner_type: NotRequired[Literal['room', 'user']] + 'The entity type of the device owner.' + password: NotRequired[str] + '[single-line only]\n\nA password required to make calls on with the device.' + phone_number: NotRequired[str] + 'The phone number associated with this device.' + port: NotRequired[int] + 'The SIP port number.' + realm: NotRequired[str] + 'The SIP realm that this device should use.' + ring_notification: NotRequired[bool] + 'A boolean indicating whether this device should ring when the user receives a call.' + sip_transport_type: NotRequired[Literal['tls']] + 'The SIP transport layer protocol.' + type: NotRequired[ + Literal[ + 'ata', + 'audiocodes', + 'c2t', + 'ciscompp', + 'dect', + 'grandstream', + 'mini', + 'mitel', + 'obi', + 'polyandroid', + 'polycom', + 'sip', + 'tickiot', + 'yealink', + ] + ] + 'User phone, or room phone.' + + +class DeskPhoneCollection(TypedDict): + """Collection of desk phones.""" + + items: NotRequired[list[DeskPhone]] + 'A list of desk phones.' diff --git a/src/dialpad/schemas/e164_format.py b/src/dialpad/schemas/e164_format.py new file mode 100644 index 0000000..ef78d2e --- /dev/null +++ b/src/dialpad/schemas/e164_format.py @@ -0,0 +1,14 @@ +from typing_extensions import NotRequired, TypedDict + + +class FormatNumberResponse(TypedDict): + """Formatted number.""" + + area_code: NotRequired[str] + 'First portion of local formatted number. e.g. "(555)"' + country_code: NotRequired[str] + 'Abbreviated country name in ISO 3166-1 alpha-2 format. e.g. "US"' + e164_number: NotRequired[str] + 'Number in local format.\n\ne.g. "(555) 555-5555"' + local_number: NotRequired[str] + 'Number in E.164 format. e.g. "+15555555555"' diff --git a/src/dialpad/schemas/faxline.py b/src/dialpad/schemas/faxline.py new file mode 100644 index 0000000..9b3faf7 --- /dev/null +++ b/src/dialpad/schemas/faxline.py @@ -0,0 +1,77 @@ +from typing import Literal, Union + +from typing_extensions import NotRequired, TypedDict + + +class ReservedLineType(TypedDict): + """Reserved number fax line assignment.""" + + number: str + 'A phone number to assign. (e164-formatted)' + type: str + 'Type of line.' + + +class SearchLineType(TypedDict): + """Search fax line assignment.""" + + area_code: str + "An area code in which to find an available phone number for assignment. If there is no area code provided, office's area code will be used." + type: str + 'Type of line.' + + +class Target(TypedDict): + """TypedDict representation of the Target schema.""" + + target_id: int + 'The ID of the target to assign the fax line to.' + target_type: Literal['department', 'user'] + 'Type of the target to assign the fax line to.' + + +class TollfreeLineType(TypedDict): + """Tollfree fax line assignment.""" + + type: str + 'Type of line.' + + +class CreateFaxNumberMessage(TypedDict): + """TypedDict representation of the CreateFaxNumberMessage schema.""" + + line: Union[ReservedLineType, SearchLineType, TollfreeLineType] + 'Line to assign.' + target: Target + 'The target to assign the number to.' + + +class FaxNumberProto(TypedDict): + """Fax number details.""" + + area_code: NotRequired[str] + 'The area code of the number.' + company_id: NotRequired[int] + 'The ID of the associated company.' + number: str + 'A mock parameter for testing.' + office_id: NotRequired[int] + 'The ID of the associate office.' + target_id: NotRequired[int] + 'The ID of the target to which this number is assigned.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The type of the target to which this number is assigned.' diff --git a/src/dialpad/schemas/group.py b/src/dialpad/schemas/group.py new file mode 100644 index 0000000..7ed846a --- /dev/null +++ b/src/dialpad/schemas/group.py @@ -0,0 +1,634 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.room import RoomProto +from dialpad.schemas.user import UserProto + + +class AddCallCenterOperatorMessage(TypedDict): + """TypedDict representation of the AddCallCenterOperatorMessage schema.""" + + keep_paid_numbers: NotRequired[bool] + 'Whether or not to keep phone numbers when switching to a support license.\n\nNote: Phone numbers require additional number licenses under a support license.' + license_type: NotRequired[Literal['agents', 'lite_support_agents']] + 'The type of license to assign to the new operator if a license is required.\n(`agents` or `lite_support_agents`). Defaults to `agents`' + role: NotRequired[Literal['admin', 'operator', 'supervisor']] + 'The role the user should assume.' + skill_level: NotRequired[int] + 'Skill level of the operator. Integer value in range 1 - 100. Default 100.' + user_id: int + 'The ID of the user.' + + +class AddOperatorMessage(TypedDict): + """TypedDict representation of the AddOperatorMessage schema.""" + + operator_id: int + 'ID of the operator to add.' + operator_type: Literal['room', 'user'] + 'Type of the operator to add. (`user` or `room`)' + role: NotRequired[Literal['admin', 'operator']] + 'The role of the new operator. (`operator` or `admin`)' + + +class AutoCallRecording(TypedDict): + """TypedDict representation of the AutoCallRecording schema.""" + + allow_pause_recording: NotRequired[bool] + 'Allow agents to stop/restart a recording during a call. Default is False.' + call_recording_inbound: NotRequired[bool] + 'Whether or not inbound calls to this call center get automatically recorded. Default is False.' + call_recording_outbound: NotRequired[bool] + 'Whether or not outbound calls from this call center get automatically recorded. Default is False.' + + +class AdvancedSettings(TypedDict): + """TypedDict representation of the AdvancedSettings schema.""" + + auto_call_recording: NotRequired[AutoCallRecording] + 'Choose which calls to and from this call center get automatically recorded. Recordings are only available to administrators of this call center, which can be found in the Dialpad app and the Calls List.' + max_wrap_up_seconds: NotRequired[int] + 'Include a post-call wrap-up time before agents can receive their next call. Default is 0.' + + +class Alerts(TypedDict): + """TypedDict representation of the Alerts schema.""" + + cc_service_level: NotRequired[int] + 'Alert supervisors when the service level drops below how many percent. Default is 95%.' + cc_service_level_seconds: NotRequired[int] + 'Inbound calls should be answered within how many seconds. Default is 60.' + + +class AvailabilityStatusProto(TypedDict): + """Availability Status for a Call Center.""" + + name: NotRequired[str] + '[single-line only]\n\nA descriptive name for the status. If the Call Center is within any holiday, it displays it.' + status: str + 'Status of this Call Center. It can be open, closed, holiday_open or holiday_closed' + + +class HoldQueueCallCenter(TypedDict): + """TypedDict representation of the HoldQueueCallCenter schema.""" + + allow_queue_callback: NotRequired[bool] + 'Whether or not to allow callers to request a callback. Default is False.' + announce_position: NotRequired[bool] + 'Whether or not to let callers know their place in the queue. This option is not available when a maximum queue wait time of less than 2 minutes is selected. Default is True.' + announcement_interval_seconds: NotRequired[int] + 'Hold announcement interval wait time. Default is 2 min.' + max_hold_count: NotRequired[int] + 'If all operators are busy on other calls, send callers to a hold queue. This is to specify your queue size. Choose from 1-1000. Default is 50.' + max_hold_seconds: NotRequired[int] + 'Maximum queue wait time in seconds. Choose from 30s to 18000s (3 hours). Default is 900s (15 min).' + queue_callback_dtmf: NotRequired[str] + 'Allow callers to request a callback when the queue has more than queue_callback_threshold number of calls by pressing one of the followings: [0,1,2,3,4,5,6,7,8,9,*,#]. Default is 9.' + queue_callback_threshold: NotRequired[int] + 'Allow callers to request a callback when the queue has more than this number of calls. Default is 5.' + queue_escape_dtmf: NotRequired[str] + 'Allow callers to exit the hold queue to voicemail by pressing one of the followings:\n[0,1,2,3,4,5,6,7,8,9,*,#]. Default is *.' + stay_in_queue_after_closing: NotRequired[bool] + 'Whether or not to allow existing calls to stay in queue after the call center has closed. Default is False.' + unattended_queue: NotRequired[bool] + 'Whether or not to allow callers to be placed in your hold queue when no agents are available. Default is False.' + + +class DtmfOptions(TypedDict): + """DTMF routing options.""" + + action: NotRequired[ + Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + ] + 'The routing action type.' + action_target_id: NotRequired[int] + 'The ID of the target that should be dialed.' + action_target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'contact', + 'contactgroup', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The type of the target that should be dialed.' + + +class DtmfMapping(TypedDict): + """TypedDict representation of the DtmfMapping schema.""" + + input: NotRequired[str] + 'The DTMF key associated with this menu item. (0-9)' + options: NotRequired[DtmfOptions] + 'The action that should be taken if the input key is pressed.' + + +class RoutingOptionsInner(TypedDict): + """Group routing options for open or closed states.""" + + action: Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + 'The action that should be taken if no operators are available.' + action_target_id: NotRequired[int] + 'The ID of the Target that inbound calls should be routed to.' + action_target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'contact', + 'contactgroup', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The type of the Target that inbound calls should be routed to.' + dtmf: NotRequired[list[DtmfMapping]] + 'DTMF menu options.' + operator_routing: NotRequired[ + Literal['fixedorder', 'longestidle', 'mostskilled', 'random', 'roundrobin', 'simultaneous'] + ] + 'The routing strategy that should be used when dialing operators.' + try_dial_operators: bool + 'Whether operators should be dialed on inbound calls.' + + +class RoutingOptions(TypedDict): + """Group routing options.""" + + closed: RoutingOptionsInner + 'Routing options to use during off hours.' + open: RoutingOptionsInner + 'Routing options to use during open hours.' + + +class VoiceIntelligence(TypedDict): + """TypedDict representation of the VoiceIntelligence schema.""" + + allow_pause: NotRequired[bool] + 'Allow individual users to start and stop Vi during calls. Default is True.' + auto_start: NotRequired[bool] + 'Auto start Vi for this call center. Default is True.' + + +class CallCenterProto(TypedDict): + """Call center.""" + + advanced_settings: NotRequired[AdvancedSettings] + 'Configure call center advanced settings.' + alerts: NotRequired[Alerts] + 'Set when alerts will be triggered.' + availability_status: NotRequired[Literal['closed', 'holiday_closed', 'holiday_open', 'open']] + 'Availability status of the group.' + country: NotRequired[str] + 'The country in which the user group resides.' + first_action: NotRequired[Literal['menu', 'operators']] + 'The initial action to take upon receiving a new call.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"]' + group_description: NotRequired[str] + 'The description of the call center.' + hold_queue: NotRequired[HoldQueueCallCenter] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + id: NotRequired[int] + 'The ID of the group entity.' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: NotRequired[str] + '[single-line only]\n\nThe name of the group.' + no_operators_action: NotRequired[ + Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + ] + 'The action to take if there are no operators available to accept an inbound call.' + office_id: NotRequired[int] + 'The ID of the office in which this group resides.' + phone_numbers: NotRequired[list[str]] + 'A list of phone numbers belonging to this group.' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The current enablement state of this group.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"]' + timezone: NotRequired[str] + 'The timezone of the group.' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"]' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"]' + + +class CallCenterCollection(TypedDict): + """Collection of call centers.""" + + cursor: NotRequired[str] + 'A cursor string that can be used to fetch the subsequent page.' + items: NotRequired[list[CallCenterProto]] + 'A list containing the first page of results.' + + +class CallCenterStatusProto(TypedDict): + """Status information for a Call Center.""" + + availability: AvailabilityStatusProto + 'Availability of the Call Center.' + capacity: int + 'The number of available operators.' + longest_call_wait_time: int + 'The longest queued call, in seconds.' + on_duty_operators: int + 'The amount of operators On Duty' + pending: int + 'The number of on-hold calls.' + + +class CreateCallCenterMessage(TypedDict): + """TypedDict representation of the CreateCallCenterMessage schema.""" + + advanced_settings: NotRequired[AdvancedSettings] + 'Configure advanced call center settings.' + alerts: NotRequired[Alerts] + 'Set when alerts will be triggered.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"].' + group_description: NotRequired[str] + 'The description of the call center. Max 256 characters.' + hold_queue: NotRequired[HoldQueueCallCenter] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: str + '[single-line only]\n\nThe name of the call center. Max 100 characters.' + office_id: int + 'The id of the office to which the call center belongs..' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"].' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"].' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"].' + + +class HoldQueueDepartment(TypedDict): + """TypedDict representation of the HoldQueueDepartment schema.""" + + allow_queuing: NotRequired[bool] + 'Whether or not send callers to a hold queue, if all operators are busy on other calls. Default is False.' + max_hold_count: NotRequired[int] + 'If all operators are busy on other calls, send callers to a hold queue. This is to specify your queue size. Choose from 1-50. Default is 50.' + max_hold_seconds: NotRequired[int] + 'Maximum queue wait time in seconds. Choose from 30s to 18000s (3 hours). Default is 900s (15 min).' + + +class CreateDepartmentMessage(TypedDict): + """TypedDict representation of the CreateDepartmentMessage schema.""" + + auto_call_recording: NotRequired[bool] + 'Whether or not automatically record all calls of this department. Default is False.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"].' + group_description: NotRequired[str] + 'The description of the department. Max 256 characters.' + hold_queue: NotRequired[HoldQueueDepartment] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the department wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: str + '[single-line only]\n\nThe name of the department. Max 100 characters.' + office_id: int + 'The id of the office to which the department belongs..' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"].' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"].' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"].' + + +class DepartmentProto(TypedDict): + """Department.""" + + auto_call_recording: NotRequired[bool] + 'Whether or not automatically record all calls of this department. Default is False.' + availability_status: NotRequired[Literal['closed', 'holiday_closed', 'holiday_open', 'open']] + 'Availability status of the group.' + country: NotRequired[str] + 'The country in which the user group resides.' + first_action: NotRequired[Literal['menu', 'operators']] + 'The initial action to take upon receiving a new call.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"]' + group_description: NotRequired[str] + 'The description of the call center.' + hold_queue: NotRequired[HoldQueueDepartment] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + id: NotRequired[int] + 'The ID of the group entity.' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: NotRequired[str] + '[single-line only]\n\nThe name of the group.' + no_operators_action: NotRequired[ + Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + ] + 'The action to take if there are no operators available to accept an inbound call.' + office_id: NotRequired[int] + 'The ID of the office in which this group resides.' + phone_numbers: NotRequired[list[str]] + 'A list of phone numbers belonging to this group.' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The current enablement state of this group.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"]' + timezone: NotRequired[str] + 'The timezone of the group.' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"]' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"]' + + +class DepartmentCollection(TypedDict): + """Collection of departments.""" + + cursor: NotRequired[str] + 'A cursor string that can be used to fetch the subsequent page.' + items: NotRequired[list[DepartmentProto]] + 'A list containing the first page of results.' + + +class OperatorCollection(TypedDict): + """Operators can be users or rooms.""" + + rooms: NotRequired[list[RoomProto]] + 'A list of rooms that can currently act as operators for this group.' + users: NotRequired[list[UserProto]] + 'A list of users who are currently operators of this group.' + + +class OperatorDutyStatusProto(TypedDict): + """TypedDict representation of the OperatorDutyStatusProto schema.""" + + duty_status_reason: NotRequired[str] + '[single-line only]\n\nA description of this status.' + duty_status_started: NotRequired[int] + 'The time stamp, in UTC, when the current on duty status changed.' + on_duty: NotRequired[bool] + 'Whether the operator is currently on duty or off duty.' + on_duty_started: NotRequired[int] + 'The time stamp, in UTC, when this operator became available for contact center calls.' + on_duty_status: NotRequired[ + Literal['available', 'busy', 'occupied', 'occupied-end', 'unavailable', 'wrapup', 'wrapup-end'] + ] + "A description of operator's on duty status." + user_id: NotRequired[int] + 'The ID of the operator.' + + +class OperatorSkillLevelProto(TypedDict): + """TypedDict representation of the OperatorSkillLevelProto schema.""" + + call_center_id: NotRequired[int] + "The call center's id." + skill_level: NotRequired[int] + 'New skill level of the operator.' + user_id: NotRequired[int] + 'The ID of the operator.' + + +class RemoveCallCenterOperatorMessage(TypedDict): + """TypedDict representation of the RemoveCallCenterOperatorMessage schema.""" + + user_id: int + 'ID of the operator to remove.' + + +class RemoveOperatorMessage(TypedDict): + """TypedDict representation of the RemoveOperatorMessage schema.""" + + operator_id: int + 'ID of the operator to remove.' + operator_type: Literal['room', 'user'] + 'Type of the operator to remove (`user` or `room`).' + + +class UpdateCallCenterMessage(TypedDict): + """TypedDict representation of the UpdateCallCenterMessage schema.""" + + advanced_settings: NotRequired[AdvancedSettings] + 'Configure advanced call center settings.' + alerts: NotRequired[Alerts] + 'Set when alerts will be triggered.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"].' + group_description: NotRequired[str] + 'The description of the call center. Max 256 characters.' + hold_queue: NotRequired[HoldQueueCallCenter] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the call center wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: NotRequired[str] + '[single-line only]\n\nThe name of the call center. Max 100 characters.' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"].' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"].' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"].' + + +class UpdateDepartmentMessage(TypedDict): + """TypedDict representation of the UpdateDepartmentMessage schema.""" + + auto_call_recording: NotRequired[bool] + 'Whether or not automatically record all calls of this department. Default is False.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"].' + group_description: NotRequired[str] + 'The description of the department. Max 256 characters.' + hold_queue: NotRequired[HoldQueueDepartment] + 'Configure how the calls are sent to a hold queue when all operators are busy on other calls.' + hours_on: NotRequired[bool] + 'The time frame when the department wants to receive calls. Default value is false, which means the call center will always take calls (24/7).' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: NotRequired[str] + '[single-line only]\n\nThe name of the department. Max 100 characters.' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds. Default is 30 seconds.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"].' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"].' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"].' + + +class UpdateOperatorDutyStatusMessage(TypedDict): + """TypedDict representation of the UpdateOperatorDutyStatusMessage schema.""" + + duty_status_reason: NotRequired[str] + '[single-line only]\n\nA description of this status.' + on_duty: bool + 'True if this status message indicates an "on-duty" status.' + + +class UpdateOperatorSkillLevelMessage(TypedDict): + """TypedDict representation of the UpdateOperatorSkillLevelMessage schema.""" + + skill_level: int + 'New skill level to set the operator in the call center. It must be an integer value between 0 and 100.' + + +class UserOrRoomProto(TypedDict): + """Operator.""" + + company_id: NotRequired[int] + 'The company to which this entity belongs.' + country: NotRequired[str] + 'The country in which the entity resides.' + id: NotRequired[int] + 'The ID of this entity.' + image_url: NotRequired[str] + "The url of this entity's profile image." + is_on_duty: NotRequired[bool] + 'Whether the entity is currently acting as an operator.' + name: NotRequired[str] + "[single-line only]\n\nThe entity's name." + office_id: NotRequired[int] + 'The office in which this entity resides.' + phone_numbers: NotRequired[list[str]] + 'The phone numbers associated with this entity.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The current enablement state of this entity.' diff --git a/src/dialpad/schemas/member_channel.py b/src/dialpad/schemas/member_channel.py new file mode 100644 index 0000000..30f0ebb --- /dev/null +++ b/src/dialpad/schemas/member_channel.py @@ -0,0 +1,33 @@ +from typing_extensions import NotRequired, TypedDict + + +class AddChannelMemberMessage(TypedDict): + """Input to add members to a channel""" + + user_id: int + 'The user id.' + + +class MembersProto(TypedDict): + """Channel member.""" + + id: NotRequired[int] + 'The user id.' + name: NotRequired[str] + '[single-line only]\n\nThe user name.' + + +class MembersCollection(TypedDict): + """Collection of channel members.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[MembersProto]] + 'A list of membser from channels.' + + +class RemoveChannelMemberMessage(TypedDict): + """Input to remove members from a channel""" + + user_id: int + 'The user id.' diff --git a/src/dialpad/schemas/number.py b/src/dialpad/schemas/number.py new file mode 100644 index 0000000..18a9909 --- /dev/null +++ b/src/dialpad/schemas/number.py @@ -0,0 +1,185 @@ +from typing import Literal, Union + +from typing_extensions import NotRequired, TypedDict + + +class AreaCodeSwap(TypedDict): + """Swap number with a number in the specified area code.""" + + area_code: NotRequired[str] + 'An area code in which to find an available phone number for assignment.' + type: str + 'Type of swap.' + + +class AssignNumberMessage(TypedDict): + """TypedDict representation of the AssignNumberMessage schema.""" + + area_code: NotRequired[str] + 'An area code in which to find an available phone number for assignment.' + number: NotRequired[str] + 'A phone number to assign. (e164-formatted)' + primary: NotRequired[bool] + 'A boolean indicating whether this should become the primary phone number.' + + +class AssignNumberTargetGenericMessage(TypedDict): + """TypedDict representation of the AssignNumberTargetGenericMessage schema.""" + + area_code: NotRequired[str] + 'An area code in which to find an available phone number for assignment.' + number: NotRequired[str] + 'A phone number to assign. (e164-formatted)' + primary: NotRequired[bool] + "A boolean indicating whether this should become the target's primary phone number." + target_id: int + 'The ID of the target to reassign this number to.' + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + 'The type of the target.' + + +class AssignNumberTargetMessage(TypedDict): + """TypedDict representation of the AssignNumberTargetMessage schema.""" + + primary: NotRequired[bool] + "A boolean indicating whether this should become the target's primary phone number." + target_id: int + 'The ID of the target to reassign this number to.' + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + 'The type of the target.' + + +class AutoSwap(TypedDict): + """Swap number with an auto-assigned number.""" + + type: str + 'Type of swap.' + + +class NumberProto(TypedDict): + """Number details.""" + + area_code: NotRequired[str] + 'The area code of the number.' + company_id: NotRequired[int] + 'The ID of the associated company.' + deleted: NotRequired[bool] + 'A boolean indicating whether this number has been ported out of Dialpad.' + number: NotRequired[str] + 'The e164-formatted number.' + office_id: NotRequired[int] + 'The ID of the associate office.' + status: NotRequired[ + Literal[ + 'available', + 'call_center', + 'call_router', + 'department', + 'dynamic_caller', + 'office', + 'pending', + 'porting', + 'room', + 'user', + ] + ] + 'The current assignment status of this number.' + target_id: NotRequired[int] + 'The ID of the target to which this number is assigned.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The type of the target to which this number is assigned.' + type: NotRequired[Literal['free', 'local', 'mobile', 'softbank', 'tollfree']] + 'The number type.' + + +class NumberCollection(TypedDict): + """Collection of numbers.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[NumberProto]] + 'A list of phone numbers.' + + +class ProvidedNumberSwap(TypedDict): + """Swap number with provided number.""" + + number: NotRequired[str] + 'A phone number to swap. (e164-formatted)' + type: str + 'Type of swap.' + + +class Target(TypedDict): + """TypedDict representation of the Target schema.""" + + target_id: int + 'The ID of the target to swap number.' + target_type: Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + 'The type of the target.' + + +class SwapNumberMessage(TypedDict): + """TypedDict representation of the SwapNumberMessage schema.""" + + swap_details: NotRequired[Union[AreaCodeSwap, AutoSwap, ProvidedNumberSwap]] + 'Type of number swap (area_code, auto, provided_number).' + target: Target + 'The target for swap number.' + + +class UnassignNumberMessage(TypedDict): + """TypedDict representation of the UnassignNumberMessage schema.""" + + number: str + 'A phone number to unassign. (e164-formatted)' diff --git a/src/dialpad/schemas/oauth.py b/src/dialpad/schemas/oauth.py new file mode 100644 index 0000000..19f48a9 --- /dev/null +++ b/src/dialpad/schemas/oauth.py @@ -0,0 +1,46 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class AuthorizationCodeGrantBodySchema(TypedDict): + """Used to redeem an access token via authorization code.""" + + client_id: NotRequired[str] + 'The client_id of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.' + client_secret: NotRequired[str] + 'The client_secret of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.' + code: str + 'The authorization code that resulted from the oauth2 authorization redirect.' + code_verifier: NotRequired[str] + 'The PKCE code verifier corresponding to the initial PKCE code challenge, if applicable.' + grant_type: Literal['authorization_code'] + 'The type of OAuth grant which is being requested.' + + +class AuthorizeTokenResponseBodySchema(TypedDict): + """TypedDict representation of the AuthorizeTokenResponseBodySchema schema.""" + + access_token: NotRequired[str] + 'A static access token.' + expires_in: NotRequired[int] + 'The number of seconds after which the access token will become expired.' + id_token: NotRequired[str] + 'User ID token (if using OpenID Connect)' + refresh_token: NotRequired[str] + 'The refresh token that can be used to obtain a new token pair when this one expires.' + token_type: NotRequired[str] + 'The type of the access_token being issued.' + + +class RefreshTokenGrantBodySchema(TypedDict): + """Used to exchange a refresh token for a short-lived access token and another refresh token.""" + + client_id: NotRequired[str] + 'The client_id of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.' + client_secret: NotRequired[str] + 'The client_secret of the oauth app.\n\nNote: must either be provided in the request body, or in a basic authorization header.' + grant_type: Literal['refresh_token'] + 'The type of OAuth grant which is being requested.' + refresh_token: str + 'The current refresh token which is being traded in for a new token pair.' diff --git a/src/dialpad/schemas/office.py b/src/dialpad/schemas/office.py new file mode 100644 index 0000000..fcb35ab --- /dev/null +++ b/src/dialpad/schemas/office.py @@ -0,0 +1,334 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.group import RoutingOptions, VoiceIntelligence +from dialpad.schemas.plan import BillingContactMessage, BillingPointOfContactMessage, PlanProto + + +class E911Message(TypedDict): + """E911 address.""" + + address: str + '[single-line only]\n\nLine 1 of the E911 address.' + address2: NotRequired[str] + '[single-line only]\n\nLine 2 of the E911 address.' + city: str + '[single-line only]\n\nCity of the E911 address.' + country: str + 'Country of the E911 address.' + state: str + '[single-line only]\n\nState or Province of the E911 address.' + zip: str + '[single-line only]\n\nZip code of the E911 address.' + + +class CreateOfficeMessage(TypedDict): + """Secondary Office creation.""" + + annual_commit_monthly_billing: bool + "A flag indicating if the primary office's plan is categorized as annual commit monthly billing." + auto_call_recording: NotRequired[bool] + 'Whether or not automatically record all calls of this office. Default is False.' + billing_address: BillingContactMessage + 'The billing address of this created office.' + billing_contact: NotRequired[BillingPointOfContactMessage] + 'The billing contact information of this created office.' + country: Literal[ + 'AR', + 'AT', + 'AU', + 'BD', + 'BE', + 'BG', + 'BH', + 'BR', + 'CA', + 'CH', + 'CI', + 'CL', + 'CN', + 'CO', + 'CR', + 'CY', + 'CZ', + 'DE', + 'DK', + 'DO', + 'DP', + 'EC', + 'EE', + 'EG', + 'ES', + 'FI', + 'FR', + 'GB', + 'GH', + 'GR', + 'GT', + 'HK', + 'HR', + 'HU', + 'ID', + 'IE', + 'IL', + 'IN', + 'IS', + 'IT', + 'JP', + 'KE', + 'KH', + 'KR', + 'KZ', + 'LK', + 'LT', + 'LU', + 'LV', + 'MA', + 'MD', + 'MM', + 'MT', + 'MX', + 'MY', + 'NG', + 'NL', + 'NO', + 'NZ', + 'PA', + 'PE', + 'PH', + 'PK', + 'PL', + 'PR', + 'PT', + 'PY', + 'RO', + 'RU', + 'SA', + 'SE', + 'SG', + 'SI', + 'SK', + 'SV', + 'TH', + 'TR', + 'TW', + 'UA', + 'US', + 'UY', + 'VE', + 'VN', + 'ZA', + ] + 'The office country.' + currency: Literal['AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'NZD', 'USD'] + "The office's billing currency." + e911_address: NotRequired[E911Message] + 'The emergency address of the created office.\n\nRequired for country codes of US, CA, AU, FR, GB, NZ.' + first_action: NotRequired[Literal['menu', 'operators']] + 'The desired action when the office receives a call.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation. Default value is ["08:00", "18:00"].' + group_description: NotRequired[str] + 'The description of the office. Max 256 characters.' + hours_on: NotRequired[bool] + 'The time frame when the office wants to receive calls. Default value is false, which means the office will always take calls (24/7).' + international_enabled: NotRequired[bool] + 'A flag indicating if the primary office is able to make international phone calls.' + invoiced: bool + 'A flag indicating if the payment will be paid by invoice.' + mainline_number: NotRequired[str] + 'The mainline of the office.' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation. To specify when hours_on is set to True. e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM. Default value is ["08:00", "18:00"].' + name: str + '[single-line only]\n\nThe office name.' + no_operators_action: NotRequired[ + Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + ] + 'The action to take if there is no one available to answer calls.' + plan_period: Literal['monthly', 'yearly'] + 'The frequency at which the company will be billed.' + ring_seconds: NotRequired[int] + 'The number of seconds to allow the group line to ring before going to voicemail. Choose from 10 seconds to 45 seconds.' + routing_options: NotRequired[RoutingOptions] + 'Call routing options for this group.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation. Default is empty array.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation. Default is empty array.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation. Default value is ["08:00", "18:00"].' + timezone: NotRequired[str] + 'Timezone using a tz database name.' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation. Default value is ["08:00", "18:00"].' + unified_billing: bool + 'A flag indicating if to send a unified invoice.' + use_same_address: NotRequired[bool] + 'A flag indicating if the billing address and the emergency address are the same.' + voice_intelligence: NotRequired[VoiceIntelligence] + 'Configure voice intelligence.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation. Default value is ["08:00", "18:00"].' + + +class E911GetProto(TypedDict): + """E911 address.""" + + address: NotRequired[str] + '[single-line only]\n\nLine 1 of the E911 address.' + address2: NotRequired[str] + '[single-line only]\n\nLine 2 of the E911 address.' + city: NotRequired[str] + '[single-line only]\n\nCity of the E911 address.' + country: NotRequired[str] + 'Country of the E911 address.' + state: NotRequired[str] + '[single-line only]\n\nState or Province of the E911 address.' + zip: NotRequired[str] + '[single-line only]\n\nZip code of the E911 address.' + + +class E911UpdateMessage(TypedDict): + """TypedDict representation of the E911UpdateMessage schema.""" + + address: str + '[single-line only]\n\nLine 1 of the new E911 address.' + address2: NotRequired[str] + '[single-line only]\n\nLine 2 of the new E911 address.' + city: str + '[single-line only]\n\nCity of the new E911 address.' + country: str + 'Country of the new E911 address.' + state: str + '[single-line only]\n\nState or Province of the new E911 address.' + update_all: NotRequired[bool] + 'Update E911 for all users in this office.' + use_validated_option: NotRequired[bool] + 'Whether to use the validated address option from our service.' + zip: str + '[single-line only]\n\nZip code of the new E911 address.' + + +class OffDutyStatusesProto(TypedDict): + """Off-duty statuses.""" + + id: NotRequired[int] + 'The office ID.' + off_duty_statuses: NotRequired[list[str]] + 'The off-duty statuses configured for this office.' + + +class OfficeSettings(TypedDict): + """TypedDict representation of the OfficeSettings schema.""" + + allow_device_guest_login: NotRequired[bool] + 'Allows guests to use desk phones within the office.' + block_caller_id_disabled: NotRequired[bool] + 'Whether the block-caller-ID option is disabled.' + bridged_target_recording_allowed: NotRequired[bool] + 'Whether recordings are enabled for sub-groups of this office.\n(e.g. departments or call centers).' + disable_desk_phone_self_provision: NotRequired[bool] + 'Whether desk-phone self-provisioning is disabled.' + disable_ivr_voicemail: NotRequired[bool] + 'Whether the default IVR voicemail feature is disabled.' + no_recording_message_on_user_calls: NotRequired[bool] + 'Whether recording of user calls should be disabled.' + set_caller_id_disabled: NotRequired[bool] + 'Whether the caller-ID option is disabled.' + + +class OfficeProto(TypedDict): + """Office.""" + + availability_status: NotRequired[Literal['closed', 'holiday_closed', 'holiday_open', 'open']] + 'Availability status of the office.' + country: NotRequired[str] + 'The country in which the office is situated.' + e911_address: NotRequired[E911GetProto] + 'The e911 address of the office.' + first_action: NotRequired[Literal['menu', 'operators']] + 'The desired action when the office receives a call.' + friday_hours: NotRequired[list[str]] + 'The Friday hours of operation.' + id: NotRequired[int] + "The office's id." + is_primary_office: NotRequired[bool] + 'A flag indicating if the office is a primary office of its company.' + monday_hours: NotRequired[list[str]] + 'The Monday hours of operation.\n(e.g. ["08:00", "12:00", "14:00", "18:00"] => open from 8AM to Noon, and from 2PM to 6PM.)' + name: NotRequired[str] + '[single-line only]\n\nThe name of the office.' + no_operators_action: NotRequired[ + Literal[ + 'bridge_target', + 'company_directory', + 'department', + 'directory', + 'disabled', + 'extension', + 'menu', + 'message', + 'operator', + 'person', + 'scripted_ivr', + 'voicemail', + ] + ] + 'The action to take if there is no one available to answer calls.' + office_id: NotRequired[int] + "The office's id." + office_settings: NotRequired[OfficeSettings] + 'Office-specific settings object.' + phone_numbers: NotRequired[list[str]] + 'The phone number(s) assigned to this office.' + ring_seconds: NotRequired[int] + 'The number of seconds to ring the main line before going to voicemail.\n(or an other-wise-specified no_operators_action).' + routing_options: NotRequired[RoutingOptions] + 'Specific call routing action to take when the office is open or closed.' + saturday_hours: NotRequired[list[str]] + 'The Saturday hours of operation.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The enablement-state of the office.' + sunday_hours: NotRequired[list[str]] + 'The Sunday hours of operation.' + thursday_hours: NotRequired[list[str]] + 'The Thursday hours of operation.' + timezone: NotRequired[str] + 'Timezone of the office.' + tuesday_hours: NotRequired[list[str]] + 'The Tuesday hours of operation.' + wednesday_hours: NotRequired[list[str]] + 'The Wednesday hours of operation.' + + +class OfficeCollection(TypedDict): + """Collection of offices.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[OfficeProto]] + 'A list of offices.' + + +class OfficeUpdateResponse(TypedDict): + """Office update.""" + + office: NotRequired[OfficeProto] + 'The updated office object.' + plan: NotRequired[PlanProto] + 'The updated office plan object.' diff --git a/src/dialpad/schemas/plan.py b/src/dialpad/schemas/plan.py new file mode 100644 index 0000000..5371bec --- /dev/null +++ b/src/dialpad/schemas/plan.py @@ -0,0 +1,102 @@ +from typing_extensions import NotRequired, TypedDict + + +class AvailableLicensesProto(TypedDict): + """Available licenses.""" + + additional_number_lines: NotRequired[int] + 'The number of additional-number lines allocated for this plan.\n\nadditional-number lines are consumed when multiple numbers are assigned to a target. i.e. if any callable entity has more than one direct number, one additional-number line is consumed for each number after the first number. This line type is available for all account types.' + contact_center_lines: NotRequired[int] + 'The number of contact-center lines allocated for this plan.\n\nContact-center lines are consumed for new users that can serve as call center agents, but does\n*not* include a primary number for the user. This line type is only available for pro and enterprise accounts.' + fax_lines: NotRequired[int] + 'The number of fax lines allocated for this plan.\n\nFax lines are consumed when a fax number is assigned to a user, office, department etc. Fax lines can be used with or without a physical fax machine, as received faxes are exposed as PDFs in the Dialpad app. This line type is available for all account types.' + room_lines: NotRequired[int] + 'The number of room lines allocated for this plan.\n\nRoom lines are consumed when a new room with a dedicated number is created. This line type is available for all account types.' + sell_lines: NotRequired[int] + 'The number of sell lines allocated for this plan.\n\nSell lines are consumed for new users that can serve as call center agents and includes a primary number for that user. This line type is only available for pro and enterprise accounts.' + talk_lines: NotRequired[int] + 'The number of talk lines allocated for this plan.\n\nTalk lines are consumed when a new user with a primary number is created. This line type is available for all account types, and does not include the ability for the user to be a call center agent.' + tollfree_additional_number_lines: NotRequired[int] + 'The number of toll-free-additional-number lines allocated for this plan.\n\nThese are functionally equivalent to additional-number lines, except that the number is a toll-free number. This line type is available for all account types.' + tollfree_room_lines: NotRequired[int] + "The number of toll-free room lines allocated for this plan.\n\nThese are functionally equivalent to room lines, except that the room's primary number is a toll-free number (subsequent numbers for a given room will still consume additional-number/toll-free-additional-number lines rather than multiple room lines). This line type is available for all account types." + tollfree_uberconference_lines: NotRequired[int] + "The number of toll-free uberconference lines allocated for this plan.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types." + uberconference_lines: NotRequired[int] + "The number of uberconference lines available for this office.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types." + + +class BillingContactMessage(TypedDict): + """Billing contact.""" + + address_line_1: str + '[single-line only]\n\nThe first line of the billing address.' + address_line_2: NotRequired[str] + '[single-line only]\n\nThe second line of the billing address.' + city: str + '[single-line only]\n\nThe billing address city.' + country: str + 'The billing address country.' + postal_code: str + '[single-line only]\n\nThe billing address postal code.' + region: str + '[single-line only]\n\nThe billing address region.' + + +class BillingContactProto(TypedDict): + """TypedDict representation of the BillingContactProto schema.""" + + address_line_1: NotRequired[str] + '[single-line only]\n\nThe first line of the billing address.' + address_line_2: NotRequired[str] + '[single-line only]\n\nThe second line of the billing address.' + city: NotRequired[str] + '[single-line only]\n\nThe billing address city.' + country: NotRequired[str] + 'The billing address country.' + postal_code: NotRequired[str] + '[single-line only]\n\nThe billing address postal code.' + region: NotRequired[str] + '[single-line only]\n\nThe billing address region.' + + +class BillingPointOfContactMessage(TypedDict): + """TypedDict representation of the BillingPointOfContactMessage schema.""" + + email: str + 'The contact email.' + name: str + '[single-line only]\n\nThe contact name.' + phone: NotRequired[str] + 'The contact phone number.' + + +class PlanProto(TypedDict): + """Billing plan.""" + + additional_number_lines: NotRequired[int] + 'The number of additional-number lines allocated for this plan.\n\nadditional-number lines are consumed when multiple numbers are assigned to a target. i.e. if any callable entity has more than one direct number, one additional-number line is consumed for each number after the first number. This line type is available for all account types.' + balance: NotRequired[str] + 'The remaining balance for this plan.\n\nThe balance will be expressed as string-encoded floating point values and will be provided in terms of USD.' + contact_center_lines: NotRequired[int] + 'The number of contact-center lines allocated for this plan.\n\nContact-center lines are consumed for new users that can serve as call center agents, but does\n*not* include a primary number for the user. This line type is only available for pro and enterprise accounts.' + fax_lines: NotRequired[int] + 'The number of fax lines allocated for this plan.\n\nFax lines are consumed when a fax number is assigned to a user, office, department etc. Fax lines can be used with or without a physical fax machine, as received faxes are exposed as PDFs in the Dialpad app. This line type is available for all account types.' + next_billing_date: NotRequired[int] + 'The UTC timestamp of the start of the next billing cycle.' + ppu_address: NotRequired[BillingContactProto] + 'The "Place of Primary Use" address.' + room_lines: NotRequired[int] + 'The number of room lines allocated for this plan.\n\nRoom lines are consumed when a new room with a dedicated number is created. This line type is available for all account types.' + sell_lines: NotRequired[int] + 'The number of sell lines allocated for this plan.\n\nSell lines are consumed for new users that can serve as call center agents and includes a primary number for that user. This line type is only available for pro and enterprise accounts.' + talk_lines: NotRequired[int] + 'The number of talk lines allocated for this plan.\n\nTalk lines are consumed when a new user with a primary number is created. This line type is available for all account types, and does not include the ability for the user to be a call center agent.' + tollfree_additional_number_lines: NotRequired[int] + 'The number of toll-free-additional-number lines allocated for this plan.\n\nThese are functionally equivalent to additional-number lines, except that the number is a toll-free number. This line type is available for all account types.' + tollfree_room_lines: NotRequired[int] + "The number of toll-free room lines allocated for this plan.\n\nThese are functionally equivalent to room lines, except that the room's primary number is a toll-free number (subsequent numbers for a given room will still consume additional-number/toll-free-additional-number lines rather than multiple room lines). This line type is available for all account types." + tollfree_uberconference_lines: NotRequired[int] + "The number of toll-free uberconference lines allocated for this plan.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types." + uberconference_lines: NotRequired[int] + "The number of uberconference lines available for this office.\n\nUberconference lines are consumed when a direct number is allocated for a User's uberconference room. This line type is available for all account types." diff --git a/src/dialpad/schemas/recording_share_link.py b/src/dialpad/schemas/recording_share_link.py new file mode 100644 index 0000000..0a5ba9b --- /dev/null +++ b/src/dialpad/schemas/recording_share_link.py @@ -0,0 +1,42 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CreateRecordingShareLink(TypedDict): + """TypedDict representation of the CreateRecordingShareLink schema.""" + + privacy: NotRequired[Literal['admin', 'company', 'owner', 'public']] + 'The privacy state of the recording share link.' + recording_id: str + "The recording entity's ID." + recording_type: Literal['admincallrecording', 'callrecording', 'voicemail'] + 'The type of the recording entity shared via the link.' + + +class RecordingShareLink(TypedDict): + """Recording share link.""" + + access_link: NotRequired[str] + 'The access link where recording can be listened or downloaded.' + call_id: NotRequired[int] + "The call's id." + created_by_id: NotRequired[int] + 'The ID of the target who created the link.' + date_added: NotRequired[str] + 'The date when the recording share link is created.' + id: NotRequired[str] + "The recording share link's ID." + item_id: NotRequired[str] + 'The ID of the recording entity shared via the link.' + privacy: NotRequired[Literal['admin', 'company', 'owner', 'public']] + 'The privacy state of the recording share link.' + type: NotRequired[Literal['admincallrecording', 'callrecording', 'voicemail']] + 'The type of the recording entity shared via the link.' + + +class UpdateRecordingShareLink(TypedDict): + """TypedDict representation of the UpdateRecordingShareLink schema.""" + + privacy: Literal['admin', 'company', 'owner', 'public'] + 'The privacy state of the recording share link.' diff --git a/src/dialpad/schemas/room.py b/src/dialpad/schemas/room.py new file mode 100644 index 0000000..30b954c --- /dev/null +++ b/src/dialpad/schemas/room.py @@ -0,0 +1,73 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CreateInternationalPinProto(TypedDict): + """Input to create a PIN for protected international calls from room.""" + + customer_ref: NotRequired[str] + '[single-line only]\n\nAn identifier to be printed in the usage summary. Typically used for identifying the person who requested the PIN.' + + +class CreateRoomMessage(TypedDict): + """TypedDict representation of the CreateRoomMessage schema.""" + + name: str + '[single-line only]\n\nThe name of the room.' + office_id: int + 'The office in which this room resides.' + + +class InternationalPinProto(TypedDict): + """Full response body for get pin operation.""" + + customer_ref: NotRequired[str] + '[single-line only]\n\nAn identifier to be printed in the usage summary. Typically used for identifying the person who requested the PIN.' + expires_on: NotRequired[str] + 'A time after which the PIN will no longer be valid.' + pin: NotRequired[str] + 'A PIN that must be entered to make international calls.' + + +class RoomProto(TypedDict): + """Room.""" + + company_id: NotRequired[int] + "The ID of this room's company." + country: NotRequired[str] + 'The country in which the room resides.' + id: NotRequired[int] + 'The ID of the room.' + image_url: NotRequired[str] + 'The profile image to use when displaying this room in the Dialpad app.' + is_free: NotRequired[bool] + 'A boolean indicating whether this room is consuming a license with an associated cost.' + is_on_duty: NotRequired[bool] + 'A boolean indicating whether this room is actively acting as an operator.' + name: NotRequired[str] + '[single-line only]\n\nThe name of the room.' + office_id: NotRequired[int] + "The ID of this room's office." + phone_numbers: NotRequired[list[str]] + 'The phone numbers assigned to this room.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The current enablement state of this room.' + + +class RoomCollection(TypedDict): + """Collection of rooms.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[RoomProto]] + 'A list of rooms.' + + +class UpdateRoomMessage(TypedDict): + """TypedDict representation of the UpdateRoomMessage schema.""" + + name: NotRequired[str] + '[single-line only]\n\nThe name of the room.' + phone_numbers: NotRequired[list[str]] + 'A list of all phone numbers assigned to the room.\n\nNumbers can be re-ordered or removed from this list to unassign them.' diff --git a/src/dialpad/schemas/schedule_reports.py b/src/dialpad/schemas/schedule_reports.py new file mode 100644 index 0000000..7335129 --- /dev/null +++ b/src/dialpad/schemas/schedule_reports.py @@ -0,0 +1,106 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class ProcessScheduleReportsMessage(TypedDict): + """TypedDict representation of the ProcessScheduleReportsMessage schema.""" + + at: int + 'Hour of the day when the report will execute considering the frequency and timezones between 0 and 23 e.g. 10 will be 10:00 am.' + coaching_group: NotRequired[bool] + 'Whether the the statistics should be for trainees of the coach group with the given target_id.' + enabled: NotRequired[bool] + 'Whether or not this schedule reports event subscription is enabled.' + endpoint_id: int + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + frequency: Literal['daily', 'monthly', 'weekly'] + 'How often the report will execute.' + name: str + '[single-line only]\n\nThe name of the schedule reports.' + on_day: int + 'The day of the week or month when the report will execute considering the frequency. daily=0, weekly=0-6, monthly=0-30.' + report_type: Literal[ + 'call_logs', 'daily_statistics', 'recordings', 'user_statistics', 'voicemails' + ] + 'The type of report that will be generated.' + target_id: NotRequired[int] + "The target's id." + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "Target's type." + timezone: NotRequired[str] + 'Timezone using a tz database name.' + + +class ScheduleReportsStatusEventSubscriptionProto(TypedDict): + """Schedule report status event subscription.""" + + at: NotRequired[int] + 'Hour of the day when the report will execute considering the frequency and timezones between 0 and 23 e.g. 10 will be 10:00 am.' + coaching_group: NotRequired[bool] + 'Whether the the statistics should be for trainees of the coach group with the given target_id.' + enabled: NotRequired[bool] + 'Whether or not the this agent status event subscription is enabled.' + frequency: NotRequired[str] + 'The frequency of the schedule reports.' + id: NotRequired[int] + "The schedule reports subscription's ID, which is generated after creating an schedule reports subscription successfully." + name: NotRequired[str] + '[single-line only]\n\nThe day to be send the schedule reports.' + on_day: NotRequired[int] + 'The day of the week or month when the report will execute considering the frequency. daily=0, weekly=0-6, monthly=0-30.' + report_type: Literal[ + 'call_logs', 'daily_statistics', 'recordings', 'user_statistics', 'voicemails' + ] + 'The report options filters.' + target_id: NotRequired[int] + "The target's id." + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'company', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "Target's type." + timezone: NotRequired[str] + 'Timezone using a tz database name.' + webhook: NotRequired[WebhookProto] + "The webhook's ID, which is generated after creating a webhook successfully." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class ScheduleReportsCollection(TypedDict): + """Schedule reports collection.""" + + cursor: NotRequired[str] + 'A token used to return the next page of results.' + items: NotRequired[list[ScheduleReportsStatusEventSubscriptionProto]] + 'A list of schedule reports.' diff --git a/src/dialpad/schemas/screen_pop.py b/src/dialpad/schemas/screen_pop.py new file mode 100644 index 0000000..5178279 --- /dev/null +++ b/src/dialpad/schemas/screen_pop.py @@ -0,0 +1,17 @@ +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.userdevice import UserDeviceProto + + +class InitiateScreenPopMessage(TypedDict): + """TypedDict representation of the InitiateScreenPopMessage schema.""" + + screen_pop_uri: str + 'The screen pop\'s url.\n\nMost Url should start with scheme name such as http or https. Be aware that url with userinfo subcomponent, such as\n"https://username:password@www.example.com" is not supported for security reasons. Launching native apps is also supported through a format such as "customuri://domain.com"' + + +class InitiateScreenPopResponse(TypedDict): + """Screen pop initiation.""" + + device: NotRequired[UserDeviceProto] + 'A device owned by the user.' diff --git a/src/dialpad/schemas/signature.py b/src/dialpad/schemas/signature.py new file mode 100644 index 0000000..ad30e0b --- /dev/null +++ b/src/dialpad/schemas/signature.py @@ -0,0 +1,12 @@ +from typing_extensions import NotRequired, TypedDict + + +class SignatureProto(TypedDict): + """Signature settings.""" + + algo: NotRequired[str] + 'The hash algorithm used to compute the signature.' + secret: NotRequired[str] + '[single-line only]\n\nThe secret string that will be used to sign the payload.' + type: NotRequired[str] + 'The signature token type.\n\n(i.e. `jwt`)' diff --git a/src/dialpad/schemas/sms.py b/src/dialpad/schemas/sms.py new file mode 100644 index 0000000..6338bec --- /dev/null +++ b/src/dialpad/schemas/sms.py @@ -0,0 +1,120 @@ +from typing import Annotated, Literal + +from typing_extensions import NotRequired, TypedDict + + +class SMSProto(TypedDict): + """SMS message.""" + + contact_id: NotRequired[str] + 'The ID of the specific contact which SMS should be sent to.' + created_date: NotRequired[str] + 'Date of SMS creation.' + device_type: NotRequired[ + Literal[ + 'android', + 'ata', + 'audiocodes', + 'c2t', + 'ciscompp', + 'dect', + 'dpmroom', + 'grandstream', + 'harness', + 'iframe_cti_extension', + 'iframe_front', + 'iframe_hubspot', + 'iframe_ms_teams', + 'iframe_open_cti', + 'iframe_salesforce', + 'iframe_service_titan', + 'iframe_zendesk', + 'ipad', + 'iphone', + 'mini', + 'mitel', + 'msteams', + 'native', + 'obi', + 'packaged_app', + 'polyandroid', + 'polycom', + 'proxy', + 'public_api', + 'salesforce', + 'sip', + 'tickiot', + 'web', + 'yealink', + ] + ] + 'The device type.' + direction: NotRequired[Literal['inbound', 'outbound']] + 'SMS direction.' + from_number: NotRequired[str] + 'The phone number from which the SMS was sent.' + id: NotRequired[int] + 'The ID of the SMS.' + message_delivery_result: NotRequired[ + Literal[ + 'accepted', + 'internal_error', + 'invalid_destination', + 'invalid_source', + 'no_route', + 'not_supported', + 'rejected', + 'rejected_spam', + 'time_out', + ] + ] + 'The final message delivery result.' + message_status: NotRequired[Literal['failed', 'pending', 'success']] + 'The status of the SMS.' + target_id: NotRequired[int] + "The target's id." + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "Target's type." + text: NotRequired[str] + 'The contents of the message that was sent.' + to_numbers: NotRequired[list[str]] + 'Up to 10 E164-formatted phone numbers who received the SMS.' + user_id: NotRequired[int] + 'The ID of the user who sent the SMS.' + + +class SendSMSMessage(TypedDict): + """TypedDict representation of the SendSMSMessage schema.""" + + channel_hashtag: NotRequired[str] + '[single-line only]\n\nThe hashtag of the channel which should receive the SMS.' + from_number: NotRequired[str] + 'The number of who sending the SMS. The number must be assigned to user or a user group. It will override user_id and sender_group_id.' + infer_country_code: NotRequired[bool] + "If true, to_numbers will be assumed to be from the specified user's country, and the E164 format requirement will be relaxed." + media: NotRequired[Annotated[str, 'base64']] + 'Base64-encoded media attachment (will cause the message to be sent as MMS).\n(Max 500 KiB raw file size)' + sender_group_id: NotRequired[int] + 'The ID of an office, department, or call center that the User should send the message on behalf of.' + sender_group_type: NotRequired[Literal['callcenter', 'department', 'office']] + "The sender group's type (i.e. office, department, or callcenter)." + text: NotRequired[str] + 'The contents of the message that should be sent.' + to_numbers: NotRequired[list[str]] + 'Up to 10 E164-formatted phone numbers who should receive the SMS.' + user_id: NotRequired[int] + 'The ID of the user who should be the sender of the SMS.' diff --git a/src/dialpad/schemas/sms_event_subscription.py b/src/dialpad/schemas/sms_event_subscription.py new file mode 100644 index 0000000..59e80ed --- /dev/null +++ b/src/dialpad/schemas/sms_event_subscription.py @@ -0,0 +1,118 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.webhook import WebhookProto +from dialpad.schemas.websocket import WebsocketProto + + +class CreateSmsEventSubscription(TypedDict): + """TypedDict representation of the CreateSmsEventSubscription schema.""" + + direction: Literal['all', 'inbound', 'outbound'] + 'The SMS direction this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the SMS event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully." + include_internal: NotRequired[bool] + 'Whether or not to trigger SMS events for SMS sent between two users from the same company.' + status: NotRequired[bool] + 'Whether or not to update on each SMS delivery status.' + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "The target's type." + + +class SmsEventSubscriptionProto(TypedDict): + """TypedDict representation of the SmsEventSubscriptionProto schema.""" + + direction: NotRequired[Literal['all', 'inbound', 'outbound']] + 'The SMS direction this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the SMS event subscription is enabled.' + id: NotRequired[int] + 'The ID of the SMS event subscription.' + include_internal: NotRequired[bool] + 'Whether or not to trigger SMS events for SMS sent between two users from the same company.' + status: NotRequired[bool] + 'Whether or not to update on each SMS delivery status.' + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "The target's type." + webhook: NotRequired[WebhookProto] + "The webhook that's associated with this event subscription." + websocket: NotRequired[WebsocketProto] + "The websocket's ID, which is generated after creating a webhook successfully." + + +class SmsEventSubscriptionCollection(TypedDict): + """Collection of sms event subscriptions.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[SmsEventSubscriptionProto]] + 'A list of SMS event subscriptions.' + + +class UpdateSmsEventSubscription(TypedDict): + """TypedDict representation of the UpdateSmsEventSubscription schema.""" + + direction: NotRequired[Literal['all', 'inbound', 'outbound']] + 'The SMS direction this event subscription subscribes to.' + enabled: NotRequired[bool] + 'Whether or not the SMS event subscription is enabled.' + endpoint_id: NotRequired[int] + "The logging endpoint's ID, which is generated after creating a webhook or websocket successfully. If you plan to pair this event subscription with another logging endpoint,\nplease provide a valid webhook ID here." + include_internal: NotRequired[bool] + 'Whether or not to trigger SMS events for SMS sent between two users from the same company.' + status: NotRequired[bool] + 'Whether or not to update on each SMS delivery status.' + target_id: NotRequired[int] + 'The ID of the specific target for which events should be sent.' + target_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "The target's type." diff --git a/src/dialpad/schemas/sms_opt_out.py b/src/dialpad/schemas/sms_opt_out.py new file mode 100644 index 0000000..556e848 --- /dev/null +++ b/src/dialpad/schemas/sms_opt_out.py @@ -0,0 +1,34 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class OptOutScopeInfo(TypedDict): + """Note, this info should be present for a particular entry in the result set if and only if the given external endpoint is actually opted out (i.e. see OptOutState.opted_out documentation); in other words, this does not apply for results in the 'opted_back_in' state.""" + + opt_out_scope_level: Literal['a2p_campaign', 'company'] + 'Scope level that the external endpoint is opted out of.' + scope_id: int + 'Unique ID of the scope entity (Company or A2P Campaign).\n\nNote, this refers to the ID assigned to this entity by Dialpad, as opposed to the TCR-assigned id.' + + +class SmsOptOutEntryProto(TypedDict): + """Individual sms-opt-out list entry.""" + + date: NotRequired[int] + 'An optional timestamp in (milliseconds-since-epoch UTC format) representing the time at which the given external endpoint transitioned to the opt_out_state.' + external_endpoint: str + "An E.164-formatted DID representing the 'external endpoint' used to contact the 'external user'\n." + opt_out_scope_info: NotRequired[OptOutScopeInfo] + "Description of the scope of communications that this external endpoint is opted out from.\n\nAs explained in the OptOutScopeInfo documentation, this must be provided if this list entry describes an endpoint that is opted out of some scope (indicated by the value of 'opt_out_state'). If the 'opt_out_state' for this entry is not 'opted_out', then this parameter will be excluded entirely or set to a null value.\n\nFor SMS opt-out-import requests: in the A2P-campaign-scope case, opt_out_scope_info.id must refer to the id of a valid, registered A2P campaign entity owned by this company. In the company-scope case, opt_out_scope_info.id must be set to the company id." + opt_out_state: Literal['opted_back_in', 'opted_out'] + 'Opt-out state for this entry in the list.' + + +class SmsOptOutListProto(TypedDict): + """A list of sms-opt-out entries to be returned in the API response.""" + + cursor: NotRequired[str] + 'A token that can be used to return the next page of results, if there are any remaining; to fetch the next page, the requester must pass this value as an argument in a new request.' + items: NotRequired[list[SmsOptOutEntryProto]] + 'List of sms opt-out entries.' diff --git a/src/dialpad/schemas/stats.py b/src/dialpad/schemas/stats.py new file mode 100644 index 0000000..8ad77ab --- /dev/null +++ b/src/dialpad/schemas/stats.py @@ -0,0 +1,66 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class ProcessStatsMessage(TypedDict): + """TypedDict representation of the ProcessStatsMessage schema.""" + + coaching_group: NotRequired[bool] + 'Whether or not the the statistics should be for trainees of the coach group with the given target_id.' + coaching_team: NotRequired[bool] + 'Whether or not the the statistics should be for trainees of the coach team with the given target_id.' + days_ago_end: NotRequired[int] + 'End of the date range to get statistics for.\n\nThis is the number of days to look back relative to the current day. Used in conjunction with days_ago_start to specify a range.' + days_ago_start: NotRequired[int] + 'Start of the date range to get statistics for.\n\nThis is the number of days to look back relative to the current day. Used in conjunction with days_ago_end to specify a range.' + export_type: Literal['records', 'stats'] + 'Whether to return aggregated statistics (stats), or individual rows for each record (records).\n\nNOTE: For stat_type "csat" or "dispositions", only "records" is supported.' + group_by: NotRequired[Literal['date', 'group', 'user']] + 'This param is only applicable when the stat_type is specified as call. For call stats, group calls by user per day (default), get total metrics by day, or break down by department and call center (office only).' + is_today: NotRequired[bool] + 'Whether or not the statistics are for the current day.\n\nNOTE: days_ago_start and days_ago_end are ignored if this is passed in.' + office_id: NotRequired[int] + 'ID of the office to get statistics for.\n\nIf a target_id and target_type are passed in this value is ignored and instead the target is used.' + stat_type: Literal[ + 'calls', 'csat', 'dispositions', 'onduty', 'recordings', 'screenshare', 'texts', 'voicemails' + ] + 'The type of statistics to be returned.\n\nNOTE: if the value is "csat" or "dispositions", target_id and target_type must be specified.' + target_id: NotRequired[int] + "The target's id." + target_type: NotRequired[ + Literal[ + 'callcenter', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + "Target's type." + timezone: NotRequired[str] + 'Timezone using a tz database name.' + + +class ProcessingProto(TypedDict): + """Processing status.""" + + already_started: NotRequired[bool] + 'A boolean indicating whether this request has already begun processing.' + request_id: NotRequired[str] + 'The processing request ID.' + + +class StatsProto(TypedDict): + """Stats export.""" + + download_url: NotRequired[str] + 'The URL of the resulting stats file.' + file_type: NotRequired[str] + 'The file format of the resulting stats file.' + status: NotRequired[Literal['complete', 'failed', 'processing']] + 'The current status of the processing request.' diff --git a/src/dialpad/schemas/transcript.py b/src/dialpad/schemas/transcript.py new file mode 100644 index 0000000..769325c --- /dev/null +++ b/src/dialpad/schemas/transcript.py @@ -0,0 +1,40 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class TranscriptLineProto(TypedDict): + """Transcript line.""" + + contact_id: NotRequired[str] + 'The ID of the contact who was speaking.' + content: NotRequired[str] + 'The transcribed text.' + name: NotRequired[str] + 'The name of the call participant who was speaking.' + time: NotRequired[str] + 'The time at which the line was spoken.' + type: NotRequired[ + Literal['ai_question', 'custom_moment', 'moment', 'real_time_moment', 'transcript'] + ] + 'Either "moment" or "transcript".' + user_id: NotRequired[int] + 'The ID of the user who was speaking.' + + +class TranscriptProto(TypedDict): + """Transcript.""" + + call_id: NotRequired[int] + "The call's id." + lines: NotRequired[list[TranscriptLineProto]] + 'An array of individual lines of the transcript.' + + +class TranscriptUrlProto(TypedDict): + """Transcript URL.""" + + call_id: NotRequired[int] + "The call's id." + url: NotRequired[str] + 'The url with which the call transcript can be accessed.' diff --git a/src/dialpad/schemas/uberconference/__init__.py b/src/dialpad/schemas/uberconference/__init__.py new file mode 100644 index 0000000..b29ae4b --- /dev/null +++ b/src/dialpad/schemas/uberconference/__init__.py @@ -0,0 +1 @@ +# This is an auto-generated schema package. Please do not edit it directly. diff --git a/src/dialpad/schemas/uberconference/meeting.py b/src/dialpad/schemas/uberconference/meeting.py new file mode 100644 index 0000000..6475ec6 --- /dev/null +++ b/src/dialpad/schemas/uberconference/meeting.py @@ -0,0 +1,65 @@ +from typing_extensions import NotRequired, TypedDict + + +class MeetingParticipantProto(TypedDict): + """Public API representation of an UberConference meeting participant.""" + + call_in_method: NotRequired[str] + 'The method this participant used to joined the meeting.' + display_name: NotRequired[str] + 'Name of the meeting participant.' + email: NotRequired[str] + 'The email address of the participant. (if applicable)' + is_organizer: NotRequired[bool] + "Whether or not the participant is the meeting's organizer." + name: NotRequired[str] + 'Name of the meeting participant.' + phone: NotRequired[str] + 'The number that the participant dialed in from. (if applicable)' + phone_number: NotRequired[str] + 'The number that the participant dialed in from. (if applicable)' + talk_time: NotRequired[int] + 'The amount of time this participant was speaking. (in milliseconds)' + + +class MeetingRecordingProto(TypedDict): + """Public API representation of an UberConference meeting recording.""" + + size: NotRequired[str] + 'Human-readable size of the recording files. (e.g. 14.3MB)' + url: NotRequired[str] + 'The URL of the audio recording of the meeting.' + + +class MeetingSummaryProto(TypedDict): + """Public API representation of an UberConference meeting.""" + + duration_ms: NotRequired[int] + 'The duration of the meeting in milliseconds.' + end_time: NotRequired[str] + 'The time at which the meeting was ended. (ISO-8601 format)' + host_name: NotRequired[str] + 'The name of the host of the meeting.' + id: NotRequired[str] + 'The ID of the meeting.' + participants: NotRequired[list[MeetingParticipantProto]] + 'The list of users that participated in the meeting.' + recordings: NotRequired[list[MeetingRecordingProto]] + 'A list of recordings from the meeting.' + room_id: NotRequired[str] + 'The ID of the conference room in which the meeting took place.' + start_time: NotRequired[str] + 'The time at which the first participant joined the meeting. (ISO-8601 format)' + title: NotRequired[str] + 'The name of the meeting.' + transcript_url: NotRequired[str] + 'The URL of the meeting transcript.' + + +class MeetingSummaryCollection(TypedDict): + """Collection of rooms for get all room operations.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.' + items: NotRequired[list[MeetingSummaryProto]] + 'A list of meeting summaries.' diff --git a/src/dialpad/schemas/uberconference/room.py b/src/dialpad/schemas/uberconference/room.py new file mode 100644 index 0000000..ba28d00 --- /dev/null +++ b/src/dialpad/schemas/uberconference/room.py @@ -0,0 +1,27 @@ +from typing_extensions import NotRequired, TypedDict + + +class RoomProto(TypedDict): + """Public API representation of an UberConference room.""" + + company_name: NotRequired[str] + 'The name of the company that owns the room.' + display_name: NotRequired[str] + 'The name of the room.' + email: NotRequired[str] + 'The email associated with the room owner.' + id: NotRequired[str] + 'The ID of the meeting room.' + number: NotRequired[str] + 'The e164-formatted dial-in number for the room.' + path: NotRequired[str] + 'The access URL for the meeting room.' + + +class RoomCollection(TypedDict): + """Collection of rooms for get all room operations.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.' + items: NotRequired[list[RoomProto]] + 'A list of meeting rooms.' diff --git a/src/dialpad/schemas/user.py b/src/dialpad/schemas/user.py new file mode 100644 index 0000000..51df9f7 --- /dev/null +++ b/src/dialpad/schemas/user.py @@ -0,0 +1,308 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class CreateUserMessage(TypedDict): + """TypedDict representation of the CreateUserMessage schema.""" + + auto_assign: NotRequired[bool] + 'If set to true, a number will be automatically assigned.' + email: str + "The user's email." + first_name: NotRequired[str] + "[single-line only]\n\nThe user's first name." + last_name: NotRequired[str] + "[single-line only]\n\nThe user's last name." + license: NotRequired[ + Literal[ + 'admins', + 'agents', + 'dpde_all', + 'dpde_one', + 'lite_lines', + 'lite_support_agents', + 'magenta_lines', + 'talk', + ] + ] + "The user's license type. This affects billing for the user." + office_id: int + "The user's office id." + + +class E911UpdateMessage(TypedDict): + """TypedDict representation of the E911UpdateMessage schema.""" + + address: str + '[single-line only]\n\nLine 1 of the new E911 address.' + address2: NotRequired[str] + '[single-line only]\n\nLine 2 of the new E911 address.' + city: str + '[single-line only]\n\nCity of the new E911 address.' + country: str + 'Country of the new E911 address.' + state: str + '[single-line only]\n\nState or Province of the new E911 address.' + use_validated_option: NotRequired[bool] + 'Whether to use the validated address option from our service.' + zip: str + '[single-line only]\n\nZip of the new E911 address.' + + +class GroupDetailsProto(TypedDict): + """TypedDict representation of the GroupDetailsProto schema.""" + + do_not_disturb: NotRequired[bool] + 'Whether the user is currently in do-not-disturb mode for this group.' + group_id: NotRequired[int] + 'The ID of the group.' + group_type: NotRequired[ + Literal[ + 'callcenter', + 'callrouter', + 'channel', + 'coachinggroup', + 'coachingteam', + 'department', + 'office', + 'room', + 'staffgroup', + 'unknown', + 'user', + ] + ] + 'The group type.' + role: NotRequired[Literal['admin', 'operator', 'supervisor']] + "The user's role in the group." + + +class MoveOfficeMessage(TypedDict): + """TypedDict representation of the MoveOfficeMessage schema.""" + + office_id: NotRequired[int] + "The user's office id. When provided, the user will be moved to this office." + + +class PersonaProto(TypedDict): + """Persona.""" + + caller_id: NotRequired[str] + 'Persona caller ID shown to receivers of calls from this persona.' + id: NotRequired[int] + "The user's id." + image_url: NotRequired[str] + 'Persona image URL.' + name: NotRequired[str] + '[single-line only]\n\nPersona name.' + phone_numbers: NotRequired[list[str]] + 'List of persona phone numbers.' + type: NotRequired[str] + 'Persona type.\n\n(corresponds to a target type)' + + +class PersonaCollection(TypedDict): + """Collection of personas.""" + + items: NotRequired[list[PersonaProto]] + 'A list of user personas.' + + +class PresenceStatus(TypedDict): + """TypedDict representation of the PresenceStatus schema.""" + + message: NotRequired[str] + 'The presence status message to be updated.' + provider: NotRequired[str] + 'The provider requesting the presence status update.' + type: NotRequired[Literal['conference', 'default']] + 'Predefined templates will be only used for the supported types.\n\nAccepts the following types:\n- `default` -- status message template: "{provider}: {message}"\n- `conference` -- status message template: "On {provider}: in the {message} meeting"\n\n`provider` and `message` should be chosen with the message template in mind.' + + +class SetStatusMessage(TypedDict): + """TypedDict representation of the SetStatusMessage schema.""" + + expiration: NotRequired[int] + 'The expiration of this status. None for no expiration.' + status_message: NotRequired[str] + 'The status message for the user.' + + +class SetStatusProto(TypedDict): + """Set user status.""" + + expiration: NotRequired[int] + 'The expiration of this status. None for no expiration.' + id: NotRequired[int] + "The user's id.\n\n('me' can be used if you are using a user level API key)" + status_message: NotRequired[str] + 'The status message for the user.' + + +class ToggleDNDMessage(TypedDict): + """TypedDict representation of the ToggleDNDMessage schema.""" + + do_not_disturb: bool + 'Determines if DND is ON or OFF.' + group_id: NotRequired[int] + "The ID of the group which the user's DND status will be updated for." + group_type: NotRequired[Literal['callcenter', 'department', 'office']] + "The type of the group which the user's DND status will be updated for." + + +class ToggleDNDProto(TypedDict): + """DND toggle.""" + + do_not_disturb: NotRequired[bool] + 'Boolean to tell if the user is on DND.' + group_id: NotRequired[int] + "The ID of the group which the user's DND status will be updated for." + group_type: NotRequired[Literal['callcenter', 'department', 'office']] + "The type of the group which the user's DND status will be updated for." + id: NotRequired[int] + "The user's id.\n\n('me' can be used if you are using a user level API key)" + + +class UpdateUserMessage(TypedDict): + """TypedDict representation of the UpdateUserMessage schema.""" + + admin_office_ids: NotRequired[list[int]] + 'The list of admin office IDs.\n\nThis is used to set the user as an office admin for the offices with the provided IDs.' + emails: NotRequired[list[str]] + "The user's emails.\n\nThis can be used to add, remove, or re-order emails. The first email in the list is the user's primary email." + extension: NotRequired[str] + "The user's new extension number.\n\nExtensions are optional in Dialpad and turned off by default. If you want extensions please contact support to enable them." + first_name: NotRequired[str] + "[single-line only]\n\nThe user's first name." + forwarding_numbers: NotRequired[list[str]] + "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call." + international_dialing_enabled: NotRequired[bool] + 'Whether or not the user is enabled to dial internationally.' + is_super_admin: NotRequired[bool] + 'Whether or not the user is a super admin. (company level administrator)' + job_title: NotRequired[str] + "[single-line only]\n\nThe user's job title." + keep_paid_numbers: NotRequired[bool] + 'Whether or not to keep phone numbers when switching to a support license.\n\nNote: Phone numbers require additional number licenses under a support license.' + last_name: NotRequired[str] + "[single-line only]\n\nThe user's last name." + license: NotRequired[ + Literal[ + 'admins', + 'agents', + 'dpde_all', + 'dpde_one', + 'lite_lines', + 'lite_support_agents', + 'magenta_lines', + 'talk', + ] + ] + "The user's license type.\n\nChanging this affects billing for the user. For a Sell license, specify the type as `agents`. For a Support license, specify the type as `support`." + office_id: NotRequired[int] + "The user's office id.\n\nIf provided, the user will be moved to this office. For international offices, the user must not have phone numbers assigned. Once the transfer is complete, your admin can add the phone numbers via the user assign number API. Only supported on paid accounts and there must be enough licenses to transfer the user to the destination office." + phone_numbers: NotRequired[list[str]] + 'A list of the phone number(s) assigned to this user.\n\nThis can be used to re-order or remove numbers. To assign a new number, use the assign number API instead.' + presence_status: NotRequired[PresenceStatus] + 'The presence status can be seen when you hover your mouse over the presence state indicator.\n\nNOTE: this is only used for Highfive and will be deprecated soon.\n\nPresence status will be set to "{provider}: {message}" when both are provided. Otherwise,\npresence status will be set to "{provider}".\n\n"type" is optional and presence status will only include predefined templates when "type" is provided. Please refer to the "type" parameter to check the supported types.\n\nTo clear the presence status, make an api call with the "presence_status" param set to empty or null. ex: `"presence_status": {}` or `"presence_status": null`\n\nTranslations will be available for the text in predefined templates. Translations for others should be provided.' + state: NotRequired[Literal['active', 'suspended']] + "The user's state.\n\nThis is used to suspend or re-activate a user." + + +class UserProto(TypedDict): + """User.""" + + admin_office_ids: NotRequired[list[int]] + 'A list of office IDs for which this user has admin privilages.' + company_id: NotRequired[int] + "The id of the user's company." + country: NotRequired[str] + 'The country in which the user resides.' + date_active: NotRequired[str] + 'The date when the user activated their Dialpad account.' + date_added: NotRequired[str] + 'A timestamp indicating when this user was created.' + date_first_login: NotRequired[str] + 'A timestamp indicating the first time that this user logged in to Dialpad.' + display_name: NotRequired[str] + "The user's name, for display purposes." + do_not_disturb: NotRequired[bool] + 'A boolean indicating whether the user is currently in "Do not disturb" mode.' + duty_status_reason: NotRequired[str] + '[single-line only]\n\nA description of this status.' + duty_status_started: NotRequired[str] + 'The timestamp, in UTC, when the current on duty status changed.' + emails: NotRequired[list[str]] + 'A list of email addresses belonging to this user.' + extension: NotRequired[str] + 'The extension that should be associated with this user in the company or office IVR directory.' + first_name: NotRequired[str] + '[single-line only]\n\nThe given name of the user.' + forwarding_numbers: NotRequired[list[str]] + "A list of phone numbers that should be dialed in addition to the user's Dialpad number(s)\nupon receiving a call." + group_details: NotRequired[list[GroupDetailsProto]] + 'Details regarding the groups that this user is a member of.' + id: NotRequired[int] + "The user's id." + image_url: NotRequired[str] + "The url of the user's profile image." + international_dialing_enabled: NotRequired[bool] + 'Whether or not the user is enabled to dial internationally.' + is_admin: NotRequired[bool] + 'A boolean indicating whether this user has administor privilages.' + is_available: NotRequired[bool] + 'A boolean indicating whether the user is not currently on a call.' + is_on_duty: NotRequired[bool] + 'A boolean indicating whether this user is currently acting as an operator.' + is_online: NotRequired[bool] + 'A boolean indicating whether the user currently has an active Dialpad device.' + is_super_admin: NotRequired[bool] + 'A boolean indicating whether this user has company-wide administor privilages.' + job_title: NotRequired[str] + "[single-line only]\n\nThe user's job title." + language: NotRequired[str] + 'The preferred spoken language of the user.' + last_name: NotRequired[str] + '[single-line only]\n\nThe family name of the user.' + license: NotRequired[ + Literal[ + 'admins', + 'agents', + 'dpde_all', + 'dpde_one', + 'lite_lines', + 'lite_support_agents', + 'magenta_lines', + 'talk', + ] + ] + 'The license type that has been allocated to this user.' + location: NotRequired[str] + '[single-line only]\n\nThe self-reported location of the user.' + muted: NotRequired[bool] + 'A boolean indicating whether the user has muted thier microphone.' + office_id: NotRequired[int] + "The ID of the user's office." + on_duty_started: NotRequired[str] + 'The timestamp, in UTC, when this operator became available for contact center calls.' + on_duty_status: NotRequired[ + Literal['available', 'busy', 'occupied', 'occupied-end', 'unavailable', 'wrapup', 'wrapup-end'] + ] + "A description of operator's on duty status." + phone_numbers: NotRequired[list[str]] + 'A list of phone numbers belonging to this user.' + state: NotRequired[Literal['active', 'cancelled', 'deleted', 'pending', 'suspended']] + 'The current enablement state of the user.' + status_message: NotRequired[str] + '[single-line only]\n\nA message indicating the activity that the user is currently engaged in.' + timezone: NotRequired[str] + 'The timezone that this user abides by.' + + +class UserCollection(TypedDict): + """Collection of users.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[UserProto]] + 'A list of users.' diff --git a/src/dialpad/schemas/userdevice.py b/src/dialpad/schemas/userdevice.py new file mode 100644 index 0000000..74be706 --- /dev/null +++ b/src/dialpad/schemas/userdevice.py @@ -0,0 +1,72 @@ +from typing import Literal + +from typing_extensions import NotRequired, TypedDict + + +class UserDeviceProto(TypedDict): + """Dialpad user device.""" + + app_version: NotRequired[str] + 'The device firmware version, or Dialpad app version.' + date_created: NotRequired[str] + 'The time at which this device was created.' + date_registered: NotRequired[str] + 'The most recent time at which the device registered with the backend.\n\nDevices register with the backend roughly once per hour, with the exception of mobile devices\n(iphone, ipad, android) for which this field will always be blank.' + date_updated: NotRequired[str] + 'The most recent time at which the device data was modified.' + display_name: NotRequired[str] + '[single-line only]\n\nThe name of this device.' + id: NotRequired[str] + 'The ID of the device.' + phone_number: NotRequired[str] + 'The phone number associated with this device.' + type: NotRequired[ + Literal[ + 'android', + 'ata', + 'audiocodes', + 'c2t', + 'ciscompp', + 'dect', + 'dpmroom', + 'grandstream', + 'harness', + 'iframe_cti_extension', + 'iframe_front', + 'iframe_hubspot', + 'iframe_ms_teams', + 'iframe_open_cti', + 'iframe_salesforce', + 'iframe_service_titan', + 'iframe_zendesk', + 'ipad', + 'iphone', + 'mini', + 'mitel', + 'msteams', + 'native', + 'obi', + 'packaged_app', + 'polyandroid', + 'polycom', + 'proxy', + 'public_api', + 'salesforce', + 'sip', + 'tickiot', + 'web', + 'yealink', + ] + ] + 'The device type.' + user_id: NotRequired[int] + 'The ID of the user who owns the device.' + + +class UserDeviceCollection(TypedDict): + """Collection of user devices.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request.\n\nUse the cursor provided in the previous response.' + items: NotRequired[list[UserDeviceProto]] + 'A list of user devices.' diff --git a/src/dialpad/schemas/webhook.py b/src/dialpad/schemas/webhook.py new file mode 100644 index 0000000..fd2286e --- /dev/null +++ b/src/dialpad/schemas/webhook.py @@ -0,0 +1,41 @@ +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.signature import SignatureProto + + +class CreateWebhook(TypedDict): + """TypedDict representation of the CreateWebhook schema.""" + + hook_url: str + "The webhook's URL. Triggered events will be sent to the url provided here." + secret: NotRequired[str] + "[single-line only]\n\nWebhook's signature secret that's used to confirm the validity of the request." + + +class UpdateWebhook(TypedDict): + """TypedDict representation of the UpdateWebhook schema.""" + + hook_url: NotRequired[str] + "The webhook's URL. Triggered events will be sent to the url provided here." + secret: NotRequired[str] + "[single-line only]\n\nWebhook's signature secret that's used to confirm the validity of the request." + + +class WebhookProto(TypedDict): + """Webhook.""" + + hook_url: NotRequired[str] + "The webhook's URL. Triggered events will be sent to the url provided here." + id: NotRequired[int] + "The webhook's ID, which is generated after creating a webhook successfully." + signature: NotRequired[SignatureProto] + "Webhook's signature containing the secret." + + +class WebhookCollection(TypedDict): + """Collection of webhooks.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[WebhookProto]] + 'A list of webhook objects.' diff --git a/src/dialpad/schemas/websocket.py b/src/dialpad/schemas/websocket.py new file mode 100644 index 0000000..af9c6a8 --- /dev/null +++ b/src/dialpad/schemas/websocket.py @@ -0,0 +1,37 @@ +from typing_extensions import NotRequired, TypedDict + +from dialpad.schemas.signature import SignatureProto + + +class CreateWebsocket(TypedDict): + """TypedDict representation of the CreateWebsocket schema.""" + + secret: NotRequired[str] + "[single-line only]\n\nWebsocket's signature secret that's used to confirm the validity of the request." + + +class UpdateWebsocket(TypedDict): + """TypedDict representation of the UpdateWebsocket schema.""" + + secret: NotRequired[str] + "[single-line only]\n\nWebsocket's signature secret that's used to confirm the validity of the request." + + +class WebsocketProto(TypedDict): + """Websocket.""" + + id: NotRequired[int] + "The webhook's ID, which is generated after creating a webhook successfully." + signature: NotRequired[SignatureProto] + "Webhook's signature containing the secret." + websocket_url: NotRequired[str] + "The websocket's URL. Users need to connect to this url to get event payloads via websocket." + + +class WebsocketCollection(TypedDict): + """Collection of webhooks.""" + + cursor: NotRequired[str] + 'A token used to return the next page of a previous request. Use the cursor provided in the previous response.' + items: NotRequired[list[WebsocketProto]] + 'A list of websocket objects.' diff --git a/test/__init__.py b/test/__init__.py index 21e4319..e69de29 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +0,0 @@ - -from .utils import prepare_test_resources - - -if __name__ == '__main__': - prepare_test_resources() diff --git a/test/client_gen_tests/__init__.py b/test/client_gen_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/client_gen_tests/test_client_gen_completeness.py b/test/client_gen_tests/test_client_gen_completeness.py new file mode 100644 index 0000000..1f0dc14 --- /dev/null +++ b/test/client_gen_tests/test_client_gen_completeness.py @@ -0,0 +1,412 @@ +#!/usr/bin/env python + +"""Tests to verify that the API client generation components are working correctly.""" + +import ast +import json +import logging +import os + +import pytest +from jsonschema_path import SchemaPath +from openapi_core import OpenAPI + +from cli.client_gen.annotation import spec_piece_to_annotation +from cli.client_gen.module_mapping import load_module_mapping +from cli.client_gen.resource_classes import resource_class_to_class_def, resource_path_to_class_def +from cli.client_gen.resource_methods import http_method_to_func_def +from cli.client_gen.resource_modules import resource_class_to_module_def +from cli.client_gen.resource_packages import _group_operations_by_class +from cli.client_gen.schema_classes import schema_to_typed_dict_def +from cli.client_gen.schema_modules import schemas_to_module_def + +logger = logging.getLogger(__name__) + +REPO_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) +SPEC_FILE = os.path.join(REPO_ROOT, 'dialpad_api_spec.json') + + +@pytest.fixture(scope='module') +def open_api_spec(): + """Loads the OpenAPI specification from the file.""" + return OpenAPI.from_file_path(SPEC_FILE) + + +@pytest.fixture(scope='module') +def schema_path_spec(): + """Loads the OpenAPI specification as a SchemaPath object.""" + with open(SPEC_FILE, 'r') as f: + spec_dict = json.load(f) + return SchemaPath.from_dict(spec_dict) + + +@pytest.fixture(scope='module') +def module_mapping(): + """Loads the module mapping configuration.""" + try: + return load_module_mapping() + except Exception as e: + pytest.skip(f'Could not load module mapping: {e}') + return {} + + +class TestGenerationUtilities: + """Tests for the client generation utilities.""" + + def test_spec_piece_to_annotation(self, open_api_spec): + """Test the spec_piece_to_annotation function.""" + + # Now we'll gather all the possible elements that we expect spec_piece_to_annotation to + # successfully operate on. This is more of a completeness test than a correctness test, + # but it should still be useful to ensure that the function can handle all the expected cases. + elements_to_test = [] + + for _path_key, path_schema in (open_api_spec.spec / 'paths').items(): + for _method_key, method_schema in path_schema.items(): + if 'requestBody' in method_schema: + elements_to_test.append(method_schema / 'requestBody') + if 'content' in (method_schema / 'requestBody'): + schema_element = ( + method_schema / 'requestBody' / 'content' / 'application/json' / 'schema' + ) + if 'properties' in schema_element: + for _property_key, property_schema in (schema_element / 'properties').items(): + elements_to_test.append(property_schema) + + if 'responses' in method_schema: + elements_to_test.append(method_schema / 'responses') + + if 'parameters' in method_schema: + for parameter_schema in method_schema / 'parameters': + elements_to_test.append(parameter_schema) + + # And now we'll go hunting for any bits that break. + for example_case in elements_to_test: + try: + _annotation = spec_piece_to_annotation(example_case) + except Exception as e: + logger.error(f'Error processing {example_case}: {e}') + raise + + def test_http_method_to_func_def(self, open_api_spec): + """Test the http_method_to_func_def function for all operations in the spec.""" + # Iterate through all paths and their methods in the OpenAPI spec + for path_key, path_item_spec in (open_api_spec.spec / 'paths').items(): + # path_item_spec is a Spec object representing a Path Item (e.g., /users/{id}) + # It contains Operation Objects for HTTP methods like 'get', 'post', etc. + for http_method_key, operation_spec in path_item_spec.items(): + # We are only interested in actual HTTP methods. + # Other keys like 'parameters', 'summary', 'description' might exist at this level. + if http_method_key.lower() not in [ + 'get', + 'put', + 'post', + 'delete', + 'options', + 'head', + 'patch', + 'trace', + ]: + continue + + # operation_spec is a Spec object representing an Operation + # (e.g., the details of GET /users/{id}) + try: + _generated_output = http_method_to_func_def( + operation_spec, # The Spec object for the specific operation + ) + # For this test, we're primarily ensuring that the function doesn't crash. + # A more detailed test might inspect the _generated_output. + assert _generated_output is not None, ( + f'http_method_to_func_def returned None for {http_method_key.upper()} {path_key}' + ) + + except Exception as e: + logger.error(f'Error processing operation: {http_method_key.upper()} {path_key}') + # Providing context about the operation that caused the error + # operation_spec.contents gives the raw dictionary for that part of the spec + logger.error(f'Operation Spec Contents: {operation_spec.contents()}') + logger.error(f'Exception: {e}') + raise + + def test_resource_path_to_class_def(self, open_api_spec): + """Test the resource_path_to_class_def function for all paths in the spec.""" + # Iterate through all paths in the OpenAPI spec + for path_key, path_item_spec in (open_api_spec.spec / 'paths').items(): + # path_item_spec is a SchemaPath object representing a Path Item (e.g., /users/{id}) + try: + _generated_class_def = resource_path_to_class_def(path_item_spec) + # For this test, we're primarily ensuring that the function doesn't crash + # and returns an AST ClassDef node. + assert _generated_class_def is not None, ( + f'resource_path_to_class_def returned None for path {path_key}' + ) + assert isinstance(_generated_class_def, ast.ClassDef), ( + f'resource_path_to_class_def did not return an ast.ClassDef for path {path_key}' + ) + + except Exception as e: + logger.error(f'Error processing path: {path_key}') + # Providing context about the path that caused the error + logger.error(f'Path Item Spec Contents: {path_item_spec.contents()}') + logger.error(f'Exception: {e}') + raise + + def test_schema_to_typed_dict_def(self, open_api_spec): + """Test the schema_to_typed_dict_def function for all schemas in the spec.""" + # Get the components/schemas section which contains all schema definitions + if 'components' not in open_api_spec.spec or 'schemas' not in ( + open_api_spec.spec / 'components' + ): + pytest.skip('No schemas found in the OpenAPI spec') + + schemas = open_api_spec.spec / 'components' / 'schemas' + + # Iterate through all schema definitions + for schema_name, schema in schemas.items(): + try: + # Generate TypedDict definition from the schema + typed_dict_def = schema_to_typed_dict_def(schema) + + # Verify the function doesn't crash and returns an AST ClassDef node + assert typed_dict_def is not None, ( + f'schema_to_typed_dict_def returned None for schema {schema_name}' + ) + assert isinstance(typed_dict_def, ast.ClassDef), ( + f'schema_to_typed_dict_def did not return an ast.ClassDef for schema {schema_name}' + ) + + # Verify the class has TypedDict as a base class + assert len(typed_dict_def.bases) > 0, ( + f'TypedDict class for schema {schema_name} has no base classes' + ) + assert any( + isinstance(base, ast.Name) and base.id == 'TypedDict' for base in typed_dict_def.bases + ), f'TypedDict class for schema {schema_name} does not inherit from TypedDict' + + # Check that the class has at least a body (could be just a pass statement) + assert len(typed_dict_def.body) > 0, ( + f'TypedDict class for schema {schema_name} has an empty body' + ) + + except Exception as e: + logger.error(f'Error processing schema: {schema_name}') + # Providing context about the schema that caused the error + logger.error(f'Schema Contents: {schema.contents()}') + logger.error(f'Exception: {e}') + raise + + def test_schemas_to_module_def(self, open_api_spec): + """Test the schemas_to_module_def function with appropriate schema groupings.""" + # Get the components/schemas section which contains all schema definitions + if 'components' not in open_api_spec.spec or 'schemas' not in ( + open_api_spec.spec / 'components' + ): + pytest.skip('No schemas found in the OpenAPI spec') + + all_schemas = open_api_spec.spec / 'components' / 'schemas' + + # Group schemas by their module prefix (e.g., 'protos.office.X' goes to 'office' module) + grouped_schemas = {} + + # First, group schemas by module name + for schema_name, schema in all_schemas.items(): + # Extract the module path from the schema name + module_path = '.'.join(schema_name.split('.')[:-1]) + if module_path not in grouped_schemas: + grouped_schemas[module_path] = [] + grouped_schemas[module_path].append(schema) + + # Test each module group separately + for module_path, schemas in grouped_schemas.items(): + try: + # Skip if module has no schemas (shouldn't happen but just in case) + if not schemas: + continue + + # Generate module definition from the schema group + module_def = schemas_to_module_def(schemas) + + # Verify the function returns an AST Module node + assert module_def is not None, ( + f'schemas_to_module_def returned None for module {module_path}' + ) + assert isinstance(module_def, ast.Module), ( + f'schemas_to_module_def did not return an ast.Module for module {module_path}' + ) + + # Check that the module has at least an import statement and a class definition + assert len(module_def.body) >= 2, ( + f'Module {module_path} does not contain enough statements (expected at least 2).' + ) + + except Exception as e: + logger.error(f'Error processing schemas for module: {module_path}') + logger.error(f'Number of schemas in module: {len(schemas)}') + logger.error(f'Schema names: {[s.parts[-1] for s in schemas]}') + logger.error(f'Exception: {e}') + raise + + # If we have no grouped schemas, test with all schemas as one module + if not grouped_schemas: + try: + all_schema_list = list(all_schemas.values()) + module_def = schemas_to_module_def(all_schema_list) + assert isinstance(module_def, ast.Module), 'Failed to generate module with all schemas' + except Exception as e: + logger.error(f'Error processing all schemas together: {e}') + raise + + def test_group_operations_by_class(self, schema_path_spec, module_mapping): + """Test the _group_operations_by_class function for grouping API operations by class.""" + # Skip test if mapping not available + if not module_mapping: + pytest.skip('Module mapping not available') + + # Get operations grouped by class + grouped_operations = _group_operations_by_class(schema_path_spec, module_mapping) + + # Check that we have at least one group + assert grouped_operations, 'No operations were grouped by class' + + # Check that each group contains operations + for class_name, operations in grouped_operations.items(): + assert class_name, 'Empty class name found in grouped operations' + assert operations, f'No operations found for class {class_name}' + + # Check the structure of each operation tuple + for operation_tuple in operations: + assert len(operation_tuple) == 3, ( + f'Operation tuple should have 3 elements, found {len(operation_tuple)}' + ) + operation_spec, http_method, api_path = operation_tuple + + # Check that operation_spec is a SchemaPath + assert isinstance(operation_spec, SchemaPath), ( + f'Operation spec is not a SchemaPath for {class_name}' + ) + + # Check that http_method is a valid HTTP method + assert http_method.lower() in [ + 'get', + 'put', + 'post', + 'delete', + 'patch', + 'options', + 'head', + 'trace', + ], f'Invalid HTTP method {http_method} for {class_name}' + + # Check that api_path is a string that starts with '/' + assert isinstance(api_path, str) and api_path.startswith('/'), ( + f'API path {api_path} for {class_name} is not valid' + ) + + def test_resource_class_to_class_def(self, schema_path_spec, module_mapping): + """Test the resource_class_to_class_def function for all mapped classes.""" + # Skip test if mapping not available + if not module_mapping: + pytest.skip('Module mapping not available') + + # Get operations grouped by class + grouped_operations = _group_operations_by_class(schema_path_spec, module_mapping) + + # Test each class definition generation + for class_name, operations in grouped_operations.items(): + try: + # Add the method name to each operation + operations_with_methods = [] + for op_spec, http_method, api_path in operations: + method_name = module_mapping[api_path][http_method]['method_name'] + operations_with_methods.append((op_spec, method_name, api_path)) + + # Generate class definition from the operations + class_def = resource_class_to_class_def(class_name, operations_with_methods) + + # Verify basic structure + assert class_def is not None, f'resource_class_to_class_def returned None for {class_name}' + assert isinstance(class_def, ast.ClassDef), f'Not a ClassDef for {class_name}' + assert class_def.name == class_name, f'Name mismatch: {class_def.name} vs {class_name}' + + # Check base class is DialpadResource + assert len(class_def.bases) > 0, f'No base class for {class_name}' + assert isinstance(class_def.bases[0], ast.Name), f'Base is not a Name for {class_name}' + assert class_def.bases[0].id == 'DialpadResource', ( + f'Not extending DialpadResource: {class_def.bases[0].id}' + ) + + # Check body has at least a docstring + assert len(class_def.body) > 0, f'No body statements for {class_name}' + assert isinstance(class_def.body[0], ast.Expr), f'First statement not Expr for {class_name}' + assert isinstance(class_def.body[0].value, ast.Constant), ( + f'First statement not docstring for {class_name}' + ) + + except Exception as e: + logger.error(f'Error processing class: {class_name}') + logger.error(f'Number of operations: {len(operations)}') + logger.error(f'Exception: {e}') + raise + + def test_resource_class_to_module_def(self, schema_path_spec, module_mapping): + """Test the resource_class_to_module_def function for all mapped classes.""" + # Skip test if mapping not available + if not module_mapping: + pytest.skip('Module mapping not available') + + # Get operations grouped by class + grouped_operations = _group_operations_by_class(schema_path_spec, module_mapping) + + # Test generating a module for each class + for class_name, operations in grouped_operations.items(): + try: + # Add the method name to each operation + operations_with_methods = [] + for op_spec, http_method, api_path in operations: + method_name = module_mapping[api_path][http_method]['method_name'] + operations_with_methods.append((op_spec, method_name, api_path)) + + # Generate module definition + module_def = resource_class_to_module_def( + class_name, operations_with_methods, schema_path_spec + ) + + # Verify basic structure + assert module_def is not None, ( + f'resource_class_to_module_def returned None for {class_name}' + ) + assert isinstance(module_def, ast.Module), f'Not a Module for {class_name}' + + # Check that the module has at least import statements and a class definition + assert len(module_def.body) >= 2, f'Module for {class_name} has too few statements' + + # Check for typing imports + has_typing_import = any( + isinstance(node, ast.ImportFrom) and node.module == 'typing' for node in module_def.body + ) + assert has_typing_import, f'No typing import for {class_name}' + + # Check for DialpadResource import + has_resource_import = any( + isinstance(node, ast.ImportFrom) + and node.module == 'dialpad.resources.base' + and any(alias.name == 'DialpadResource' for alias in node.names) + for node in module_def.body + ) + assert has_resource_import, f'No DialpadResource import for {class_name}' + + # Check that the class definition is included + class_defs = [node for node in module_def.body if isinstance(node, ast.ClassDef)] + assert len(class_defs) == 1, ( + f'Expected 1 class in module for {class_name}, found {len(class_defs)}' + ) + assert class_defs[0].name == class_name, ( + f'Class name mismatch: {class_defs[0].name} vs {class_name}' + ) + + except Exception as e: + logger.error(f'Error processing module for class: {class_name}') + logger.error(f'Number of operations: {len(operations)}') + logger.error(f'Exception: {e}') + raise diff --git a/test/test_client_methods.py b/test/test_client_methods.py new file mode 100644 index 0000000..0ad6104 --- /dev/null +++ b/test/test_client_methods.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python + +"""Tests to automatically detect common issues with resource definitions. + +In particular these tests will look through the files in dialpad-python-sdk/dialpad/resources/ and +ensure: + +- All subclasses of DialpadResource are exposed directly in resources/__init__.py +- All resources are available as properties of DialpadClient +- Public methods defined on the concrete subclasses only make web requests that agree with + the Dialpad API's open-api spec +""" + +import logging +from urllib.parse import parse_qs, urlparse + +import pytest +import requests +from openapi_core import OpenAPI +from openapi_core.contrib.requests import RequestsOpenAPIRequest +from openapi_core.datatypes import RequestParameters +from werkzeug.datastructures import Headers, ImmutableMultiDict + +from dialpad.client import DialpadClient +from dialpad.resources.base import DialpadResource + +from .utils import generate_faked_kwargs + +logger = logging.getLogger(__name__) + + +class RequestsMockOpenAPIRequest(RequestsOpenAPIRequest): + """ + Converts a requests-mock request to an OpenAPI request + """ + + def __init__(self, request): + self.request = request + if request.url is None: + raise RuntimeError('Request URL is missing') + self._url_parsed = urlparse(request.url, allow_fragments=False) + + self.parameters = RequestParameters( + query=ImmutableMultiDict(parse_qs(self._url_parsed.query)), + header=Headers(dict(self.request.headers)), + ) + + +# The "requests_mock" pytest fixture stubs out live requests with a schema validation check +# against the Dialpad API openapi spec. +@pytest.fixture +def openapi_stub(requests_mock): + openapi = OpenAPI.from_file_path('dialpad_api_spec.json') + + def request_matcher(request: requests.PreparedRequest): + openapi.validate_request(RequestsMockOpenAPIRequest(request)) + + # Handle pagination for /api/v2/users endpoint + if '/api/v2/users' in request.url: + parsed_url = urlparse(request.url) + query_params = parse_qs(parsed_url.query) + cursor = query_params.get('cursor', [None])[0] + + if cursor is None: + # First page: 3 users with cursor for next page + response_data = { + 'items': [ + {'id': 1, 'display_name': 'User 1'}, + {'id': 2, 'display_name': 'User 2'}, + {'id': 3, 'display_name': 'User 3'}, + ], + 'cursor': 'next_page_cursor', + } + elif cursor == 'next_page_cursor': + # Second page: 2 users, no next cursor + response_data = { + 'items': [{'id': 4, 'display_name': 'User 4'}, {'id': 5, 'display_name': 'User 5'}] + } + else: + # No more pages + response_data = {'items': []} + + fake_response = requests.Response() + fake_response.status_code = 200 + fake_response._content = str.encode(str(response_data).replace("'", '"')) + return fake_response + + # If the request is valid, return a generic fake response. + fake_response = requests.Response() + fake_response.status_code = 200 + fake_response._content = b'{"success": true}' + return fake_response + + requests_mock.add_matcher(request_matcher) + + +class TestClientResourceMethods: + """Smoketest for all the client resource methods to ensure they produce valid requests according + to the OpenAPI spec.""" + + def test_pagination_handling(self, openapi_stub): + """Verifies that the DialpadClient handles pagination.""" + + # Construct a DialpadClient with a fake API key. + dp = DialpadClient('123') + + _users = list(dp.users.list()) + assert len(_users) == 5, 'Expected to resolve exactly 5 users from paginated responses.' + + def test_request_conformance(self, openapi_stub): + """Verifies that all API requests produced by this library conform to the spec. + + Although this test cannot guarantee that the requests are semantically correct, it can at least + determine whether they are well-formed according to the OpenAPI spec. + """ + + # Construct a DialpadClient with a fake API key. + dp = DialpadClient('123') + + # Iterate through the attributes on the client object to find the API resource accessors. + for a in dir(dp): + resource_instance = getattr(dp, a) + + # Skip any attributes that are not DialpadResources + if not isinstance(resource_instance, DialpadResource): + continue + + logger.info('Verifying request format of %s methods', resource_instance.__class__.__name__) + + # Iterate through the attributes on the resource instance. + for method_attr in dir(resource_instance): + # Skip any methods and attributes that are not unique to this resource class. + if method_attr in dir(DialpadResource): + continue + + # Skip private attributes. + if method_attr.startswith('_'): + continue + + # Skip attributes that are not functions. + resource_method = getattr(resource_instance, method_attr) + if not callable(resource_method): + continue + + # Generate fake kwargs for the resource method. + faked_kwargs = generate_faked_kwargs(resource_method) + if (resource_instance.__class__.__name__, method_attr) == ('NumbersResource', 'swap'): + # The openapi validator doesn't like that swap_details can be valid under multiple + # OneOf schemas... + faked_kwargs['request_body'].pop('swap_details', None) + + if (resource_instance.__class__.__name__, method_attr) == ('FaxLinesResource', 'assign'): + # The openapi validator doesn't like it if "line" could be valid under multiple schemas. + faked_kwargs['request_body']['line'] = {'type': 'toll-free'} + + logger.info( + 'Testing resource method %s.%s with faked kwargs: %s', + resource_instance.__class__.__name__, + method_attr, + faked_kwargs, + ) + try: + # Call the resource method with the faked kwargs. + result = resource_method(**faked_kwargs) + logger.info( + 'Result of %s.%s: %s', + resource_instance.__class__.__name__, + method_attr, + result, + ) + except Exception as e: + logger.error( + 'Error calling %s.%s with faked kwargs %s: %s', + resource_instance.__class__.__name__, + method_attr, + faked_kwargs, + e, + ) + raise diff --git a/test/test_resource_sanity.py b/test/test_resource_sanity.py deleted file mode 100644 index 91e417f..0000000 --- a/test/test_resource_sanity.py +++ /dev/null @@ -1,723 +0,0 @@ -#!/usr/bin/env python - -"""Tests to automatically detect common issues with resource definitions. - -In particular these tests will look through the files in dialpad-python-sdk/dialpad/resources/ and -ensure: - -- All subclasses of DialpadResource are exposed directly in resources/__init__.py -- All resources are available as properties of DialpadClient -- Public methods defined on the concrete subclasses only make web requests that agree with - the Dialpad API's open-api spec -""" - -import inspect -import pkgutil -import pytest - -from swagger_stub import swagger_stub - -from .utils import resource_filepath, prepare_test_resources - -from dialpad.client import DialpadClient -from dialpad import resources -from dialpad.resources.resource import DialpadResource - - -# The "swagger_files_url" pytest fixture stubs out live requests with a schema validation check -# against the Dialpad API swagger spec. - -# NOTE: Responses returned by the stub will not necessarily be a convincing dummy for the responses -# returned by the live API, so some complex scenarios may not be possible to test using this -# strategy. -@pytest.fixture(scope='module') -def swagger_files_url(): - # Ensure that the spec is up-to-date first. - prepare_test_resources() - - return [ - (resource_filepath('swagger_spec.json'), 'https://dialpad.com'), - ] - - -class TestResourceSanity: - """Sanity-tests for (largely) automatically validating new and existing client API methods. - - When new API resource methods are added to the library, examples of each method must be added to - EX_METHOD_CALLS to allow the unit tests to call those methods and validate that the API requests - they generate adhere to the swagger spec. - - The example calls should generally include as many keyword arguments as possible so that any - potential mistakes in the parameter names, url path, and request body can be caught by the - schema tests. - - Entries in the "EX_METHOD_CALLS" dictionary should be of the form: - { - '': { - 'method_name': { - 'arg_name': arg_value, - 'other_arg_name': other_arg_value, - }, - 'other_method_name': etc... - } - } - """ - - EX_METHOD_CALLS = { - 'AppSettingsResource': { - 'get': { - 'target_id': '123', - 'target_type': 'office', - } - }, - 'BlockedNumberResource': { - 'list': {}, - 'block_numbers': { - 'numbers': ['+12223334444'] - }, - 'unblock_numbers': { - 'numbers': ['+12223334444'] - }, - 'get': { - 'number': '+12223334444' - }, - }, - 'CallResource': { - 'get_info': { - 'call_id': '123' - }, - 'initiate_call': { - 'phone_number': '+12223334444', - 'user_id': '123', - 'group_id': '123', - 'group_type': 'department', - 'device_id': '123', - 'custom_data': 'example custom data', - } - }, - 'CallRouterResource': { - 'list': { - 'office_id': '123', - }, - 'get': { - 'router_id': '123', - }, - 'create': { - 'name': 'Test Router', - 'routing_url': 'fakeurl.com/url', - 'office_id': '123', - 'default_target_id': '123', - 'default_target_type': 'user', - 'enabled': True, - 'secret': '123', - }, - 'patch': { - 'router_id': '123', - 'name': 'Test Router', - 'routing_url': 'fakeurl.com/url', - 'office_id': '123', - 'default_target_id': '123', - 'default_target_type': 'user', - 'enabled': True, - 'secret': '123', - }, - 'delete': { - 'router_id': '123', - }, - 'assign_number': { - 'router_id': '123', - 'area_code': '519', - }, - }, - 'CallbackResource': { - 'enqueue_callback': { - 'call_center_id': '123', - 'phone_number': '+12223334444', - }, - 'validate_callback': { - 'call_center_id': '123', - 'phone_number': '+12223334444', - }, - }, - 'CallCenterResource': { - 'get': { - 'call_center_id': '123', - }, - 'get_operators': { - 'call_center_id': '123', - }, - 'add_operator': { - 'call_center_id': '123', - 'user_id': '123', - 'skill_level': '10', - 'role': 'supervisor', - 'license_type': 'lite_support_agents', - 'keep_paid_numbers': False, - }, - 'remove_operator': { - 'call_center_id': '123', - 'user_id': '123', - } - }, - 'CompanyResource': { - 'get': {}, - }, - 'ContactResource': { - 'list': { - 'owner_id': '123', - }, - 'create': { - 'first_name': 'Testiel', - 'last_name': 'McTestersen', - 'company_name': 'ABC', - 'emails': ['tmtesten@test.com'], - 'extension': '123', - 'job_title': 'Eric the half-a-bee', - 'owner_id': '123', - 'phones': ['+12223334444'], - 'trunk_group': '123', - 'urls': ['test.com/about'], - }, - 'create_with_uid': { - 'first_name': 'Testiel', - 'last_name': 'McTestersen', - 'uid': 'UUID-updownupdownleftrightab', - 'company_name': 'ABC', - 'emails': ['tmtesten@test.com'], - 'extension': '123', - 'job_title': 'Eric the half-a-bee', - 'phones': ['+12223334444'], - 'trunk_group': '123', - 'urls': ['test.com/about'], - }, - 'delete': { - 'contact_id': '123', - }, - 'get': { - 'contact_id': '123', - }, - 'patch': { - 'contact_id': '123', - 'first_name': 'Testiel', - 'last_name': 'McTestersen', - 'company_name': 'ABC', - 'emails': ['tmtesten@test.com'], - 'extension': '123', - 'job_title': 'Eric the half-a-bee', - 'phones': ['+12223334444'], - 'trunk_group': '123', - 'urls': ['test.com/about'], - }, - }, - 'DepartmentResource': { - 'get': { - 'department_id': '123', - }, - 'get_operators': { - 'department_id': '123', - }, - 'add_operator': { - 'department_id': '123', - 'operator_id': '123', - 'operator_type': 'room', - 'role': 'operator', - }, - 'remove_operator': { - 'department_id': '123', - 'operator_id': '123', - 'operator_type': 'room', - }, - }, - 'EventSubscriptionResource': { - 'list_call_event_subscriptions': { - 'target_id': '123', - 'target_type': 'room', - }, - 'get_call_event_subscription': { - 'subscription_id': '123', - }, - 'put_call_event_subscription': { - 'subscription_id': '123', - 'url': 'test.com/subhook', - 'secret': 'badsecret', - 'enabled': True, - 'group_calls_only': False, - 'target_id': '123', - 'target_type': 'office', - 'call_states': ['connected', 'queued'], - }, - 'delete_call_event_subscription': { - 'subscription_id': '123', - }, - 'list_sms_event_subscriptions': { - 'target_id': '123', - 'target_type': 'room', - }, - 'get_sms_event_subscription': { - 'subscription_id': '123', - }, - 'put_sms_event_subscription': { - 'subscription_id': '123', - 'url': 'test.com/subhook', - 'secret': 'badsecret', - 'direction': 'outbound', - 'enabled': True, - 'target_id': '123', - 'target_type': 'office', - }, - 'delete_sms_event_subscription': { - 'subscription_id': '123', - }, - }, - 'NumberResource': { - 'list': { - 'status': 'available', - }, - 'get': { - 'number': '+12223334444', - }, - 'unassign': { - 'number': '+12223334444', - }, - 'assign': { - 'number': '+12223334444', - 'target_id': '123', - 'target_type': 'office', - }, - 'format': { - 'country_code': 'gb', - 'number': '020 3048 4377', - }, - }, - 'OfficeResource': { - 'list': {}, - 'get': { - 'office_id': '123', - }, - 'assign_number': { - 'office_id': '123', - 'number': '+12223334444', - }, - 'get_operators': { - 'office_id': '123', - }, - 'unassign_number': { - 'office_id': '123', - 'number': '+12223334444', - }, - 'get_call_centers': { - 'office_id': '123', - }, - 'get_departments': { - 'office_id': '123', - }, - 'get_plan': { - 'office_id': '123', - }, - 'update_licenses': { - 'office_id': '123', - 'fax_line_delta': '2', - }, - }, - 'RoomResource': { - 'list': { - 'office_id': '123', - }, - 'create': { - 'name': 'Where it happened', - 'office_id': '123', - }, - 'generate_international_pin': { - 'customer_ref': 'Burr, sir', - }, - 'delete': { - 'room_id': '123', - }, - 'get': { - 'room_id': '123', - }, - 'update': { - 'room_id': '123', - 'name': 'For the last tiiiime', - 'phone_numbers': ['+12223334444'], - }, - 'assign_number': { - 'room_id': '123', - 'number': '+12223334444', - }, - 'unassign_number': { - 'room_id': '123', - 'number': '+12223334444', - }, - 'get_deskphones': { - 'room_id': '123', - }, - 'create_deskphone': { - 'room_id': '123', - 'mac_address': 'Tim Cook', - 'name': 'The red one.', - 'phone_type': 'polycom', - }, - 'delete_deskphone': { - 'room_id': '123', - 'deskphone_id': '123', - }, - 'get_deskphone': { - 'room_id': '123', - 'deskphone_id': '123', - }, - }, - 'SMSResource': { - 'send_sms': { - 'user_id': '123', - 'to_numbers': ['+12223334444'], - 'text': 'Itemized list to follow.', - 'infer_country_code': False, - 'sender_group_id': '123', - 'sender_group_type': 'callcenter', - }, - }, - 'StatsExportResource': { - 'post': { - 'coaching_group': False, - 'days_ago_start': '1', - 'days_ago_end': '2', - 'is_today': False, - 'export_type': 'records', - 'stat_type': 'calls', - 'office_id': '123', - 'target_id': '123', - 'target_type': 'callcenter', - 'timezone': 'America/New_York', - }, - 'get': { - 'export_id': '123', - }, - }, - 'SubscriptionResource': { - 'list_agent_status_event_subscriptions': {}, - 'get_agent_status_event_subscription': { - 'subscription_id': '123', - }, - 'create_agent_status_event_subscription': { - 'agent_type': 'callcenter', - 'enabled': True, - 'webhook_id': '1000', - }, - 'update_agent_status_event_subscription': { - 'subscription_id': '123', - 'agent_type': 'callcenter', - 'enabled': True, - 'webhook_id': '1000', - }, - 'delete_agent_status_event_subscription': { - 'subscription_id': '123', - }, - 'list_call_event_subscriptions': { - 'target_id': '123', - 'target_type': 'room', - }, - 'get_call_event_subscription': { - 'subscription_id': '123', - }, - 'create_call_event_subscription': { - 'enabled': True, - 'group_calls_only': False, - 'target_id': '123', - 'target_type': 'office', - 'call_states': ['connected', 'queued'], - 'webhook_id': '1000', - }, - 'update_call_event_subscription': { - 'subscription_id': '123', - 'enabled': True, - 'group_calls_only': False, - 'target_id': '123', - 'target_type': 'office', - 'call_states': ['connected', 'queued'], - 'webhook_id': '1000', - }, - 'delete_call_event_subscription': { - 'subscription_id': '123', - }, - 'list_contact_event_subscriptions': {}, - 'get_contact_event_subscription': { - 'subscription_id': '123', - }, - 'create_contact_event_subscription': { - 'contact_type': 'shared', - 'enabled': True, - 'webhook_id': '1000', - }, - 'update_contact_event_subscription': { - 'subscription_id': '123', - 'contact_type': 'shared', - 'enabled': True, - 'webhook_id': '1000', - }, - 'delete_contact_event_subscription': { - 'subscription_id': '123', - }, - 'list_sms_event_subscriptions': { - 'target_id': '123', - 'target_type': 'room', - }, - 'get_sms_event_subscription': { - 'subscription_id': '123', - }, - 'create_sms_event_subscription': { - 'direction': 'outbound', - 'enabled': True, - 'target_id': '123', - 'target_type': 'office', - 'webhook_id': '1000', - }, - 'update_sms_event_subscription': { - 'subscription_id': '123', - 'direction': 'outbound', - 'enabled': True, - 'target_id': '123', - 'target_type': 'office', - 'webhook_id': '1000', - }, - 'delete_sms_event_subscription': { - 'subscription_id': '123', - }, - }, - 'TranscriptResource': { - 'get': { - 'call_id': '123', - }, - }, - 'UserResource': { - 'search': { - 'query': 'test', - 'cursor': 'iamacursor', - }, - 'list': { - 'email': 'tmtesten@test.com', - 'state': 'suspended', - }, - 'create': { - 'email': 'tmtesten@test.com', - 'office_id': '123', - 'first_name': 'Testietta', - 'last_name': 'McTestersen', - 'license': 'lite_support_agents', - }, - 'delete': { - 'user_id': '123', - }, - 'get': { - 'user_id': '123', - }, - 'update': { - 'user_id': '123', - 'admin_office_ids': ['123'], - 'emails': ['tmtesten@test.com'], - 'extension': '123', - 'first_name': 'Testietta', - 'last_name': 'McTestersen', - 'forwarding_numbers': ['+12223334444'], - 'is_super_admin': True, - 'job_title': 'Administraterar', - 'license': 'lite_lines', - 'office_id': '123', - 'phone_numbers': ['+12223334444'], - 'state': 'active', - }, - 'toggle_call_recording': { - 'user_id': '123', - 'is_recording': False, - 'play_message': True, - 'recording_type': 'group', - }, - 'assign_number': { - 'user_id': '123', - 'number': '+12223334444', - }, - 'initiate_call': { - 'user_id': '123', - 'phone_number': '+12223334444', - 'custom_data': 'Y u call self?', - 'group_id': '123', - 'group_type': 'department', - 'outbound_caller_id': 'O.0', - }, - 'unassign_number': { - 'user_id': '123', - 'number': '+12223334444', - }, - 'get_deskphones': { - 'user_id': '123', - }, - 'create_deskphone': { - 'user_id': '123', - 'mac_address': 'Tim Cook', - 'name': 'The red one.', - 'phone_type': 'polycom', - }, - 'delete_deskphone': { - 'user_id': '123', - 'deskphone_id': '123', - }, - 'get_deskphone': { - 'user_id': '123', - 'deskphone_id': '123', - }, - 'get_personas': { - 'user_id': '123', - }, - 'toggle_do_not_disturb': { - 'user_id': '123', - 'do_not_disturb': True, - }, - }, - 'UserDeviceResource': { - 'get': { - 'device_id': '123', - }, - 'list': { - 'user_id': '123', - }, - }, - 'WebhookResource': { - 'list_webhooks': {}, - 'get_webhook': { - 'webhook_id': '123', - }, - 'create_webhook': { - 'hook_url': 'https://test.com/subhook', - 'secret': 'badsecret', - }, - 'update_webhook': { - 'webhook_id': '123', - 'hook_url': 'https://test.com/subhook', - 'secret': 'badsecret', - }, - 'delete_webhook': { - 'webhook_id': '123', - }, - }, - } - - def get_method_example_kwargs(self, resource_instance, resource_method): - """Returns the appropriate kwargs to use when sanity-checking API resource methods.""" - class_msg = 'DialpadResource subclass "%s" must have an entry in EX_METHOD_CALLS' - - class_name = resource_instance.__class__.__name__ - assert class_name in self.EX_METHOD_CALLS, class_msg % class_name - - method_msg = 'Method "%s.%s" must have an entry in EX_METHOD_CALLS' - method_name = resource_method.__name__ - assert method_name in self.EX_METHOD_CALLS[class_name], method_msg % (class_name, method_name) - - return self.EX_METHOD_CALLS[class_name][method_name] - - def _get_resource_submodule_names(self): - """Returns an iterator of python modules that exist in the dialpad/resources directory.""" - for importer, modname, ispkg in pkgutil.iter_modules(resources.__path__): - if modname == 'resource': - continue - - if ispkg: - continue - - yield modname - - def _get_resource_submodules(self): - """Returns an iterator of python modules that are exposed via from dialpad.resources import *""" - for modname in self._get_resource_submodule_names(): - if hasattr(resources, modname): - yield getattr(resources, modname) - - def _get_resource_classes(self): - """Returns an iterator of DialpadResource subclasses that are exposed under dialpad.resources""" - for mod in self._get_resource_submodules(): - for k, v in mod.__dict__.items(): - if not inspect.isclass(v): - continue - - if not issubclass(v, DialpadResource): - continue - - if v == DialpadResource: - continue - - yield v - - def test_resources_properly_imported(self): - """Verifies that all modules definied in the resources directory are properly exposed under - dialpad.resources. - """ - exposed_resources = dir(resources) - - msg = '"%s" module is present in the resources directory, but is not imported in ' \ - 'resources/__init__.py' - - for modname in self._get_resource_submodule_names(): - assert modname in exposed_resources, msg % modname - - def test_resource_classes_properly_exposed(self): - """Verifies that all subclasses of DialpadResource that are defined in the resources directory - are also exposed as direct members of the resources module. - """ - exposed_resources = dir(resources) - - msg = '"%(name)s" resource class is present in the resources package, but is not exposed ' \ - 'directly as resources.%(name)s via resources/__init__.py' - - for c in self._get_resource_classes(): - assert c.__name__ in exposed_resources, msg % {'name': c.__name__} - - def test_request_conformance(self, swagger_stub): - """Verifies that all API requests produced by this library conform to the swagger spec. - - Although this test cannot guarantee that the requests are semantically correct, it can at least - determine whether they are schematically correct. - - This test will also fail if there are no test-kwargs defined in EX_METHOD_CALLS for any public - method implemented by a subclass of DialpadResource. - """ - - # Construct a DialpadClient with a fake API key. - dp = DialpadClient('123') - - # Iterate through the attributes on the client object to find the API resource accessors. - for a in dir(dp): - resource_instance = getattr(dp, a) - - # Skip any attributes that are not DialpadResources - if not isinstance(resource_instance, DialpadResource): - continue - - print('\nVerifying request format of %s methods' % - resource_instance.__class__.__name__) - - # Iterate through the attributes on the resource instance. - for method_attr in dir(resource_instance): - # Skip private attributes. - if method_attr.startswith('_'): - continue - - # Skip attributes that are not unique to this particular subclass of DialpadResource. - if hasattr(DialpadResource, method_attr): - continue - - # Skip attributes that are not functions. - resource_method = getattr(resource_instance, method_attr) - if not callable(resource_method): - continue - - # Skip attributes that are not instance methods. - arg_names = inspect.getargspec(resource_method).args - if not arg_names or arg_names[0] != 'self': - continue - - # Fetch example kwargs to test the method (and raise if they haven't been provided). - method_kwargs = self.get_method_example_kwargs(resource_instance, resource_method) - - # Call the method, and allow the swagger mock to raise an exception if it encounters a - # schema error. - print('Testing %s with kwargs: %s' % (method_attr, method_kwargs)) - resource_method(**method_kwargs) diff --git a/test/utils.py b/test/utils.py index 5b8c1c9..fa55125 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,57 +1,132 @@ -import json -import os -import requests - - -RESOURCE_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), '.resources') - - -def resource_filepath(filename): - """Returns a path to the given file name in the test resources directory.""" - return os.path.join(RESOURCE_PATH, filename) - - -def prepare_test_resources(): - """Prepares any resources that are expected to be available at test-time.""" - - if not os.path.exists(RESOURCE_PATH): - os.mkdir(RESOURCE_PATH) - - # Generate the Dialpad API swagger spec, and write it to a file for easy access. - with open(resource_filepath('swagger_spec.json'), 'w') as f: - json.dump(_generate_swagger_spec(), f) - - -def _generate_swagger_spec(): - """Downloads current Dialpad API swagger spec and returns it as a dict.""" - - # Unfortunately, a little bit of massaging is needed to appease the swagger parser. - def _hotpatch_spec_piece(piece): - if 'type' in piece: - if piece['type'] == 'string' and piece.get('format') == 'int64' and 'default' in piece: - piece['default'] = str(piece['default']) - - if 'operationId' in piece and 'parameters' in piece: - for sub_p in piece['parameters']: - sub_p['required'] = sub_p.get('required', False) - - if 'basePath' in piece: - del piece['basePath'] - - def _hotpatch_spec(spec): - if isinstance(spec, dict): - _hotpatch_spec_piece(spec) - for k, v in spec.items(): - _hotpatch_spec(v) - - elif isinstance(spec, list): - for v in spec: - _hotpatch_spec(v) - - return spec - - # Download the spec from dialpad.com. - spec_json = requests.get('https://dialpad.com/static/openapi/apiv2openapi-en.json').json() - - # Return a patched version that will satisfy the swagger lib. - return _hotpatch_spec(spec_json) +import inspect +import logging +from typing import Annotated, Any, Callable, List, Literal, Union, get_args, get_origin + +from faker import Faker +from typing_extensions import NotRequired + +fake = Faker() +logger = logging.getLogger(__name__) + + +def generate_faked_kwargs(func: Callable) -> dict[str, Any]: + """ + Generates a dictionary of keyword arguments for a given function. + + This function inspects the signature of the input function and uses the Faker + library to generate mock data for each parameter based on its type annotation. + It supports standard types, lists, and nested TypedDicts. + + Args: + func: The function for which to generate kwargs. + + Returns: + A dictionary of keyword arguments that can be used to call the function. + """ + kwargs = {} + signature = inspect.signature(func) + params = signature.parameters + + for name, param in params.items(): + annotation = param.annotation + if annotation is not inspect.Parameter.empty: + kwargs[name] = _generate_fake_data(annotation) + else: + # Handle cases where there's no type hint with a default or warning + print(f"Warning: No type annotation for parameter '{name}'. Skipping.") + + return kwargs + + +def _is_typed_dict(type_hint: Any) -> bool: + """Checks if a type hint is a TypedDict.""" + return ( + inspect.isclass(type_hint) + and issubclass(type_hint, dict) + and hasattr(type_hint, '__annotations__') + ) + + +def _unwrap_not_required(type_hint: Any) -> Any: + """ + Unwraps NotRequired annotations to get the underlying type. + + Args: + type_hint: The type annotation that might be wrapped in NotRequired. + + Returns: + The unwrapped type or the original type if not NotRequired. + """ + origin = get_origin(type_hint) + if origin is NotRequired: + args = get_args(type_hint) + return args[0] if args else type_hint + return type_hint + + +def _generate_fake_data(type_hint: Any) -> Any: + """ + Recursively generates fake data based on the provided type hint. + + Args: + type_hint: The type annotation for which to generate data. + + Returns: + Generated fake data corresponding to the type hint. + """ + # Unwrap NotRequired annotations first + type_hint = _unwrap_not_required(type_hint) + + # Handle basic types + if type_hint is int: + return fake.pyint() + if type_hint is str: + return fake.word() + if type_hint is float: + return fake.pyfloat() + if type_hint is bool: + return fake.boolean() + if type_hint is list or type_hint is List: + # Generate a list of 1-5 strings for a generic list + return [fake.word() for _ in range(fake.pyint(min_value=1, max_value=5))] + + # Handle typing.List[some_type], Literal, Optional, and Union + origin = get_origin(type_hint) or getattr(type_hint, '__origin__', None) + args = get_args(type_hint) or getattr(type_hint, '__args__', None) + + if origin is Annotated and args[-1] == 'base64': + return 'DEADBEEF' # Placeholder for base64-encoded data + + # Handle Literal types + if origin is Literal and args: + return fake.random_element(elements=args) + + # Handle Optional types (which are Union[T, None]) + if origin is Union and args: + # Filter out NoneType from Union args + non_none_args = [arg for arg in args if arg is not type(None)] + if len(non_none_args) == 1: + # This is Optional[T] - generate data for T with 80% probability + if fake.boolean(chance_of_getting_true=80): + return _generate_fake_data(non_none_args[0]) + return None + # For general Union types, pick a random non-None type + if non_none_args: + chosen_type = fake.random_element(elements=non_none_args) + return _generate_fake_data(chosen_type) + + if origin in (list, List) and args: + inner_type = args[0] + # Generate a list of 1-5 elements of the specified inner type + return [_generate_fake_data(inner_type) for _ in range(fake.pyint(min_value=1, max_value=5))] + + # Handle TypedDict + if _is_typed_dict(type_hint): + typed_dict_data = {} + for field_name, field_type in type_hint.__annotations__.items(): + typed_dict_data[field_name] = _generate_fake_data(field_type) + return typed_dict_data + + # Fallback for unhandled types + logger.warning(f"Unhandled type '{type_hint}'. Returning None.") + return None diff --git a/tools/create_release.sh b/tools/create_release.sh deleted file mode 100755 index 650c1dc..0000000 --- a/tools/create_release.sh +++ /dev/null @@ -1,257 +0,0 @@ -#!/bin/bash - -DOC="Build and release a new version of the python-dialpad package. - -Usage: - create_version.sh [-h] [--patch|--minor|--major] [--bump-only|--no-upload] - -By default, this script will: -1 - Run the tests -2 - Check that we're on master -3 - Bump the patch-number in setup.py -4 - Check whether there are any remote changes that haven't been pulled -5 - Build the distribution package -6 - Verify the package integrity -7 - Commit the patch-number bump -8 - Tag the release -9 - Upload the package to PyPI -10 - Push the commit and tag to github - -If anything fails along the way, the script will bail out. - -Options: - -h --help Show this message - --patch Bump the patch version number (default) - --minor Bump the minor version number - --major Bump the major version number - --bump-only Make the appropriate version bump, but don't do anything else - (i.e. stop after performing step 3) - --no-upload Do everything other than uploading the package to PyPI - (i.e. skip step 9) -" -# docopt parser below, refresh this parser with `docopt.sh create_release.sh` -# shellcheck disable=2016,1075 -docopt() { parse() { if ${DOCOPT_DOC_CHECK:-true}; then local doc_hash -doc_hash=$(printf "%s" "$DOC" | shasum -a 256) -if [[ ${doc_hash:0:5} != "$digest" ]]; then -stderr "The current usage doc (${doc_hash:0:5}) does not match \ -what the parser was generated with (${digest}) -Run \`docopt.sh\` to refresh the parser."; _return 70; fi; fi; local root_idx=$1 -shift; argv=("$@"); parsed_params=(); parsed_values=(); left=(); testdepth=0 -local arg; while [[ ${#argv[@]} -gt 0 ]]; do if [[ ${argv[0]} = "--" ]]; then -for arg in "${argv[@]}"; do parsed_params+=('a'); parsed_values+=("$arg"); done -break; elif [[ ${argv[0]} = --* ]]; then parse_long -elif [[ ${argv[0]} = -* && ${argv[0]} != "-" ]]; then parse_shorts -elif ${DOCOPT_OPTIONS_FIRST:-false}; then for arg in "${argv[@]}"; do -parsed_params+=('a'); parsed_values+=("$arg"); done; break; else -parsed_params+=('a'); parsed_values+=("${argv[0]}"); argv=("${argv[@]:1}"); fi -done; local idx; if ${DOCOPT_ADD_HELP:-true}; then -for idx in "${parsed_params[@]}"; do [[ $idx = 'a' ]] && continue -if [[ ${shorts[$idx]} = "-h" || ${longs[$idx]} = "--help" ]]; then -stdout "$trimmed_doc"; _return 0; fi; done; fi -if [[ ${DOCOPT_PROGRAM_VERSION:-false} != 'false' ]]; then -for idx in "${parsed_params[@]}"; do [[ $idx = 'a' ]] && continue -if [[ ${longs[$idx]} = "--version" ]]; then stdout "$DOCOPT_PROGRAM_VERSION" -_return 0; fi; done; fi; local i=0; while [[ $i -lt ${#parsed_params[@]} ]]; do -left+=("$i"); ((i++)) || true; done -if ! required "$root_idx" || [ ${#left[@]} -gt 0 ]; then error; fi; return 0; } -parse_shorts() { local token=${argv[0]}; local value; argv=("${argv[@]:1}") -[[ $token = -* && $token != --* ]] || _return 88; local remaining=${token#-} -while [[ -n $remaining ]]; do local short="-${remaining:0:1}" -remaining="${remaining:1}"; local i=0; local similar=(); local match=false -for o in "${shorts[@]}"; do if [[ $o = "$short" ]]; then similar+=("$short") -[[ $match = false ]] && match=$i; fi; ((i++)) || true; done -if [[ ${#similar[@]} -gt 1 ]]; then -error "${short} is specified ambiguously ${#similar[@]} times" -elif [[ ${#similar[@]} -lt 1 ]]; then match=${#shorts[@]}; value=true -shorts+=("$short"); longs+=(''); argcounts+=(0); else value=false -if [[ ${argcounts[$match]} -ne 0 ]]; then if [[ $remaining = '' ]]; then -if [[ ${#argv[@]} -eq 0 || ${argv[0]} = '--' ]]; then -error "${short} requires argument"; fi; value=${argv[0]}; argv=("${argv[@]:1}") -else value=$remaining; remaining=''; fi; fi; if [[ $value = false ]]; then -value=true; fi; fi; parsed_params+=("$match"); parsed_values+=("$value"); done -}; parse_long() { local token=${argv[0]}; local long=${token%%=*} -local value=${token#*=}; local argcount; argv=("${argv[@]:1}") -[[ $token = --* ]] || _return 88; if [[ $token = *=* ]]; then eq='='; else eq='' -value=false; fi; local i=0; local similar=(); local match=false -for o in "${longs[@]}"; do if [[ $o = "$long" ]]; then similar+=("$long") -[[ $match = false ]] && match=$i; fi; ((i++)) || true; done -if [[ $match = false ]]; then i=0; for o in "${longs[@]}"; do -if [[ $o = $long* ]]; then similar+=("$long"); [[ $match = false ]] && match=$i -fi; ((i++)) || true; done; fi; if [[ ${#similar[@]} -gt 1 ]]; then -error "${long} is not a unique prefix: ${similar[*]}?" -elif [[ ${#similar[@]} -lt 1 ]]; then -[[ $eq = '=' ]] && argcount=1 || argcount=0; match=${#shorts[@]} -[[ $argcount -eq 0 ]] && value=true; shorts+=(''); longs+=("$long") -argcounts+=("$argcount"); else if [[ ${argcounts[$match]} -eq 0 ]]; then -if [[ $value != false ]]; then -error "${longs[$match]} must not have an argument"; fi -elif [[ $value = false ]]; then -if [[ ${#argv[@]} -eq 0 || ${argv[0]} = '--' ]]; then -error "${long} requires argument"; fi; value=${argv[0]}; argv=("${argv[@]:1}") -fi; if [[ $value = false ]]; then value=true; fi; fi; parsed_params+=("$match") -parsed_values+=("$value"); }; required() { local initial_left=("${left[@]}") -local node_idx; ((testdepth++)) || true; for node_idx in "$@"; do -if ! "node_$node_idx"; then left=("${initial_left[@]}"); ((testdepth--)) || true -return 1; fi; done; if [[ $((--testdepth)) -eq 0 ]]; then -left=("${initial_left[@]}"); for node_idx in "$@"; do "node_$node_idx"; done; fi -return 0; }; either() { local initial_left=("${left[@]}"); local best_match_idx -local match_count; local node_idx; ((testdepth++)) || true -for node_idx in "$@"; do if "node_$node_idx"; then -if [[ -z $match_count || ${#left[@]} -lt $match_count ]]; then -best_match_idx=$node_idx; match_count=${#left[@]}; fi; fi -left=("${initial_left[@]}"); done; ((testdepth--)) || true -if [[ -n $best_match_idx ]]; then "node_$best_match_idx"; return 0; fi -left=("${initial_left[@]}"); return 1; }; optional() { local node_idx -for node_idx in "$@"; do "node_$node_idx"; done; return 0; }; switch() { local i -for i in "${!left[@]}"; do local l=${left[$i]} -if [[ ${parsed_params[$l]} = "$2" ]]; then -left=("${left[@]:0:$i}" "${left[@]:((i+1))}") -[[ $testdepth -gt 0 ]] && return 0; if [[ $3 = true ]]; then -eval "((var_$1++))" || true; else eval "var_$1=true"; fi; return 0; fi; done -return 1; }; stdout() { printf -- "cat <<'EOM'\n%s\nEOM\n" "$1"; }; stderr() { -printf -- "cat <<'EOM' >&2\n%s\nEOM\n" "$1"; }; error() { -[[ -n $1 ]] && stderr "$1"; stderr "$usage"; _return 1; }; _return() { -printf -- "exit %d\n" "$1"; exit "$1"; }; set -e; trimmed_doc=${DOC:0:1027} -usage=${DOC:64:83}; digest=b986d; shorts=(-h '' '' '' '' '') -longs=(--help --patch --minor --major --bump-only --no-upload) -argcounts=(0 0 0 0 0 0); node_0(){ switch __help 0; }; node_1(){ -switch __patch 1; }; node_2(){ switch __minor 2; }; node_3(){ switch __major 3 -}; node_4(){ switch __bump_only 4; }; node_5(){ switch __no_upload 5; } -node_6(){ optional 0; }; node_7(){ either 1 2 3; }; node_8(){ optional 7; } -node_9(){ either 4 5; }; node_10(){ optional 9; }; node_11(){ required 6 8 10; } -node_12(){ required 11; }; cat <<<' docopt_exit() { -[[ -n $1 ]] && printf "%s\n" "$1" >&2; printf "%s\n" "${DOC:64:83}" >&2; exit 1 -}'; unset var___help var___patch var___minor var___major var___bump_only \ -var___no_upload; parse 12 "$@"; local prefix=${DOCOPT_PREFIX:-''} -local docopt_decl=1; [[ $BASH_VERSION =~ ^4.3 ]] && docopt_decl=2 -unset "${prefix}__help" "${prefix}__patch" "${prefix}__minor" \ -"${prefix}__major" "${prefix}__bump_only" "${prefix}__no_upload" -eval "${prefix}"'__help=${var___help:-false}' -eval "${prefix}"'__patch=${var___patch:-false}' -eval "${prefix}"'__minor=${var___minor:-false}' -eval "${prefix}"'__major=${var___major:-false}' -eval "${prefix}"'__bump_only=${var___bump_only:-false}' -eval "${prefix}"'__no_upload=${var___no_upload:-false}'; local docopt_i=0 -for ((docopt_i=0;docopt_i /dev/null - exit 1 -} - -confirm() { - if [ -z "$*" ]; then - read -p "Shall we proceed? (y/N)" -r confirmation - else - read -p "$*" -r confirmation - fi - if [[ ! $confirmation =~ ^[Yy]$ ]]; then - bail_out - fi - echo -} - -REPO_DIR=`dirname "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"` - -pushd $REPO_DIR &> /dev/null - -# Do a safety-confirmation if the user is about to do something that isn't trivial to undo. -if [ $__bump_only == "false" ]; then - if [ $__no_upload == "true" ]; then - echo "You're about to build and push a new ($VERSION_PART) release to Github" - echo "(Although we won't upload the package to PyPI)" - else - echo "You're about to build and push a new ($VERSION_PART) release to Github AND PyPI" - fi - confirm "Are you sure that's what you want to do? (y/N)" -fi - -# Do some sanity checks to make sure we're in a sufficient state to actually do what the user wants. - -# If we're planning to do more than just bump the version, then make sure "twine" is installed. -if [[ $__bump_only == "false" ]]; then - if ! command -v twine &> /dev/null; then - echo "You must install twine (pip install twine) if you want to upload to PyPI" - bail_out - fi -fi - -# Make sure we're on master (but let the user proceed if they reeeeally want to). -branch_name=$(git branch | sed -n -e 's/^\* \(.*\)/\1/p') -if [ "$branch_name" != "master" ]; then - echo "We probably shouldn't be bumping the version number if we're not on the master branch." - confirm "Are you this is want you want? (y/N)" -fi - -# Run the unit tests and make sure they're passing. -test_failure_prompt="Are you *entirely* sure you want to release a build with failing tests? (y/N)" -tox || confirm "There are failing tests. $test_failure_prompt" - -# If we're *only* bumping the version, then we're safe to proceed at this point. -if [ $__bump_only == "true" ]; then - tox -e bump $VERSION_PART - exit -fi - -# In any other scenario, we should make sure the working directory is clean, and that we're -# up-to-date with origin/master -if ! git pull origin master --dry-run -v 2>&1 | grep "origin/master" | grep "up to date" &> /dev/null; then - echo "There are changes that you need to pull on master." - bail_out -fi - -# We'll let bump2version handle the dirty-working-directory scenario. -tox -e bump $VERSION_PART || bail_out - -# Now we need to build the package, so let's clear away any junk that might be lying around. -rm -rf ./dist &> /dev/null -rm -rf ./build &> /dev/null - -# The build stdout is a bit noisy, but stderr will be helpful if there's an error. -tox -e build > /dev/null || bail_out - -# Make sure there aren't any issues with the package. -twine check dist/* || bail_out - -# Upload the package if that's desirable. -if [ $__no_upload == "false" ]; then - twine upload dist/* || bail_out -fi - -# Finally, commit the changes, tag the commit, and push. -git add . -new_version=`cat .bumpversion.cfg | grep "current_version = " | sed "s/current_version = //g"` -git commit -m "Release version $new_version" -git tag -a "v$new_version" -m "Release version $new_version" - -git push origin master -git push origin "v$new_version" - -echo "Congrats!" -if [ $__no_upload == "true" ]; then - echo "The $new_version release commit has been pushed to GitHub, and tagged as \"v$new_version\"" -else - echo "The $new_version release is now live on PyPI, and tagged as \"v$new_version\" on GitHub" -fi - -popd &> /dev/null diff --git a/tox.ini b/tox.ini deleted file mode 100644 index ac0f1c7..0000000 --- a/tox.ini +++ /dev/null @@ -1,24 +0,0 @@ -[tox] -envlist = py2, py3 - -[testenv] -setenv = - PYTHONPATH = {toxinidir} - -deps = - -rrequirements.txt - -rdev_requirements.txt -commands = - python -m pytest --cov dialpad --disable-warnings - -[testenv:py3] -basepython = python3 - -[testenv:build] -commands = - python ./setup.py sdist bdist_wheel - -[testenv:bump] -commands = - python -m bumpversion --allow-dirty {posargs} - diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..373d51a --- /dev/null +++ b/uv.lock @@ -0,0 +1,1147 @@ +version = 1 +requires-python = ">=3.9" + +[[package]] +name = "ansicon" +version = "1.89.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b6/e2/1c866404ddbd280efedff4a9f15abfe943cb83cde6e895022370f3a61f85/ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1", size = 67312 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/f9/f1c10e223c7b56a38109a3f2eb4e7fe9a757ea3ed3a166754fb30f65e466/ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec", size = 63675 }, +] + +[[package]] +name = "attrs" +version = "25.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, +] + +[[package]] +name = "blessed" +version = "1.21.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinxed", marker = "platform_system == 'Windows'" }, + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0c/5e/3cada2f7514ee2a76bb8168c71f9b65d056840ebb711962e1ec08eeaa7b0/blessed-1.21.0.tar.gz", hash = "sha256:ece8bbc4758ab9176452f4e3a719d70088eb5739798cd5582c9e05f2a28337ec", size = 6660011 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/8e/0a37e44878fd76fac9eff5355a1bf760701f53cb5c38cdcd59a8fd9ab2a2/blessed-1.21.0-py2.py3-none-any.whl", hash = "sha256:f831e847396f5a2eac6c106f4dfadedf46c4f804733574b15fe86d2ed45a9588", size = 84727 }, +] + +[[package]] +name = "cached-property" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/4b/3d870836119dbe9a5e3c9a61af8cc1a8b69d75aea564572e385882d5aefb/cached_property-2.0.1.tar.gz", hash = "sha256:484d617105e3ee0e4f1f58725e72a8ef9e93deee462222dbd51cd91230897641", size = 10574 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/0e/7d8225aab3bc1a0f5811f8e1b557aa034ac04bdf641925b30d3caf586b28/cached_property-2.0.1-py3-none-any.whl", hash = "sha256:f617d70ab1100b7bcf6e42228f9ddcb78c676ffa167278d9f730d1c2fba69ccb", size = 7428 }, +] + +[[package]] +name = "certifi" +version = "2025.4.26" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818 }, + { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649 }, + { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045 }, + { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356 }, + { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471 }, + { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317 }, + { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368 }, + { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491 }, + { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695 }, + { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849 }, + { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091 }, + { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445 }, + { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782 }, + { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794 }, + { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846 }, + { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350 }, + { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657 }, + { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260 }, + { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164 }, + { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571 }, + { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952 }, + { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959 }, + { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030 }, + { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015 }, + { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106 }, + { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402 }, + { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936 }, + { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790 }, + { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924 }, + { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626 }, + { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567 }, + { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957 }, + { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408 }, + { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399 }, + { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815 }, + { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537 }, + { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565 }, + { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357 }, + { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776 }, + { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622 }, + { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435 }, + { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653 }, + { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231 }, + { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243 }, + { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442 }, + { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147 }, + { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057 }, + { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454 }, + { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174 }, + { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166 }, + { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064 }, + { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641 }, + { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671 }, + { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744 }, + { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993 }, + { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382 }, + { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536 }, + { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349 }, + { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365 }, + { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499 }, + { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735 }, + { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786 }, + { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203 }, + { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436 }, + { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772 }, + { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "coverage" +version = "7.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/06/d1/7b18a2e0d2994e4e108dadf16580ec192e0a9c65f7456ccb82ced059f9bf/coverage-7.9.0.tar.gz", hash = "sha256:1a93b43de2233a7670a8bf2520fed8ebd5eea6a65b47417500a9d882b0533fa2", size = 813385 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/25/c83935ed228bd0ce277a9a92b505a4f67b0b15ba0344680974a77452c5dd/coverage-7.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3d494fa4256e3cb161ca1df14a91d2d703c27d60452eb0d4a58bb05f52f676e4", size = 211940 }, + { url = "https://files.pythonhosted.org/packages/36/42/c58ca1fec2a346ad12356fac955a9b6d848ab37f632a7cb1bc7476efcf90/coverage-7.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b613efceeabf242978d14e1a65626ec3be67c5261918a82a985f56c2a05475ee", size = 212329 }, + { url = "https://files.pythonhosted.org/packages/64/0a/6b61e4348cf7b0a70f7995247cde5cc4b5ef0b61d9718109896c77d9ed0e/coverage-7.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:673a4d2cb7ec78e1f2f6f41039f6785f27bca0f6bc0e722b53a58286d12754e1", size = 241447 }, + { url = "https://files.pythonhosted.org/packages/a9/1e/5f7060b909352cba70d34be0e34619659c0ddbef426665e036d5d3046b3c/coverage-7.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1edc2244932e9fed92ad14428b9480a97ecd37c970333688bd35048f6472f260", size = 239322 }, + { url = "https://files.pythonhosted.org/packages/f5/78/f4ba669c9bf15b537136b663ccb846032cfb73e28b59458ef6899f18fe07/coverage-7.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8b92a7617faa2017bd44c94583830bab8be175722d420501680abc4f5bc794", size = 240467 }, + { url = "https://files.pythonhosted.org/packages/79/38/3246ea3ac68dc6f85afac0cb0362d3703647378b9882d55796c71fe83a1a/coverage-7.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8f3ca1f128f11812d3baf0a482e7f36ffb856ac1ae14de3b5d1adcfb7af955d", size = 240376 }, + { url = "https://files.pythonhosted.org/packages/c0/58/ef1f20afbaf9affe2941e7b077a8cf08075c6e3fe5e1dfc3160908b6a1de/coverage-7.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c30eed34eb8206d9b8c2d0d9fa342fa98e10f34b1e9e1eb05f79ccbf4499c8ff", size = 239046 }, + { url = "https://files.pythonhosted.org/packages/09/ba/d510b05b3ca0da8fe746acf8ac815b2d560d6c4d5c4e0f6eafb2ec27dc33/coverage-7.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24e6f8e5f125cd8bff33593a484a079305c9f0be911f76c6432f580ade5c1a17", size = 239318 }, + { url = "https://files.pythonhosted.org/packages/82/c7/328a412e3bd78c049180df3f4374bb13a332ed8731ff66f49578d5ebf98c/coverage-7.9.0-cp310-cp310-win32.whl", hash = "sha256:a1b0317b4a8ff4d3703cd7aa642b4f963a71255abe4e878659f768238fab6602", size = 214430 }, + { url = "https://files.pythonhosted.org/packages/db/a5/0e788cc4796989d77bfb6b1c58819edc2c65522926f0c08cfe42d1529f2b/coverage-7.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:512b1ea57a11dfa23b7f3d8fe8690fcf8cd983a70ae4c2c262cf5c972618fa15", size = 215350 }, + { url = "https://files.pythonhosted.org/packages/9d/91/721a7df15263babfe89caf535a08bacbadebdef87338cf37d40f7400161b/coverage-7.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:55b7b9df45174956e0f719a56cf60c0cb4a7f155668881d00de6384e2a3402f4", size = 212055 }, + { url = "https://files.pythonhosted.org/packages/8d/d6/1f4c1eae67e698a8535ede02a6958a7587d06869d33a9b134ecc0e17ee07/coverage-7.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:87bceebbc91a58c9264c43638729fcb45910805b9f86444f93654d988305b3a2", size = 212445 }, + { url = "https://files.pythonhosted.org/packages/bd/48/c375a6e6a266efa2d5fbf9b04eac88c87430d1a337b4f383ea8beeeedd44/coverage-7.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81da3b6e289bf9fc7dc159ab6d5222f5330ac6e94a6d06f147ba46e53fa6ec82", size = 245010 }, + { url = "https://files.pythonhosted.org/packages/7a/43/ec070ad02a1ee10837555a852b6fa256f8c71a953c209488e027673fc5b6/coverage-7.9.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b361684a91224d4362879c1b1802168d2435ff76666f1b7ba52fc300ad832dbc", size = 242725 }, + { url = "https://files.pythonhosted.org/packages/fa/ff/8b8efbd058dd59b489d9c5e27ba5766e895c396dd3bd1b78bebef9808c5f/coverage-7.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9a384ea4f77ac0a7e36c9a805ed95ef10f423bdb68b4e9487646cdf548a6a05", size = 244527 }, + { url = "https://files.pythonhosted.org/packages/9d/e7/3863f458a3af009a4817656f5b56fa90c7e363d73fef338601b275e979c4/coverage-7.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:38a5642aa82ea6de0e4331e346f5ba188a9fdb7d727e00199f55031b85135d0a", size = 244174 }, + { url = "https://files.pythonhosted.org/packages/4b/f0/2ff1fa06ccd3c3d653e352b10ddeec511b018890b28dbd3c29b6ea3f742e/coverage-7.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8c5ff4ca4890c0b57d3e80850534609493280c0f9e6ea2bd314b10cb8cbd76e0", size = 242227 }, + { url = "https://files.pythonhosted.org/packages/32/e2/bae13555436f1d0278e70cfe22a0980eab9809e89361e859c96ffa788cb9/coverage-7.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cd052a0c4727ede06393da3c1df1ae6ef6c079e6bdfefb39079877404b3edc22", size = 242815 }, + { url = "https://files.pythonhosted.org/packages/20/7c/e1b5b3313c1e3a5e8f8ced567fee67f18c8f18cebee8af0d69052f445a55/coverage-7.9.0-cp311-cp311-win32.whl", hash = "sha256:f73fd1128165e1d665cb7f863a91d00f073044a672c7dfa04ab400af4d1a9226", size = 214469 }, + { url = "https://files.pythonhosted.org/packages/6a/c9/0034d3ccbb7b8f80b1ce8a927ea06e2ba265bd0ba4a9a95a83026ac78dfd/coverage-7.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd62d62e782d3add529c8e7943f5600efd0d07dadf3819e5f9917edb4acf85d8", size = 215407 }, + { url = "https://files.pythonhosted.org/packages/f1/e1/7473bf679a43638c5ccba6228f45f68d33c3b7414ffae757dbb0bb2f1127/coverage-7.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:f75288785cc9a67aff3b04dafd8d0f0be67306018b224d319d23867a161578d6", size = 213778 }, + { url = "https://files.pythonhosted.org/packages/dd/6b/7bdef79e79076c7e3303ce2453072528ed13988210fb7a8702bb3d98ea8c/coverage-7.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:969ed1ed0ab0325b50af3204f9024782180e64fb281f5a2952f479ec60a02aba", size = 212252 }, + { url = "https://files.pythonhosted.org/packages/08/fe/7e08dd50c3c3cfdbe822ee11e24da9f418983faefb4f5e52fbffae5beeb2/coverage-7.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1abd41781c874e716aaeecb8b27db5f4f2bc568f2ed8d41228aa087d567674f0", size = 212491 }, + { url = "https://files.pythonhosted.org/packages/d4/65/9793cf61b3e4c5647e70aabd5b9470958ffd341c42f90730beeb4d21af9c/coverage-7.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0eb6e99487dffd28c88a4fc2ea4286beaf0207a43388775900c93e56cc5a8ae3", size = 246294 }, + { url = "https://files.pythonhosted.org/packages/2a/c9/fc61695132da06a34b27a49e853010a80d66a5534a1dfa770cb38aca71c0/coverage-7.9.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c425c85ddb62b32d44f83fb20044fe32edceceee1db1f978c062eec020a73ea5", size = 243311 }, + { url = "https://files.pythonhosted.org/packages/62/0e/559a86887580d0de390e018bddfa632ae0762eeeb065bb5557f319071527/coverage-7.9.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0a1f7676bc90ceba67caa66850d689947d586f204ccf6478400c2bf39da5790", size = 245503 }, + { url = "https://files.pythonhosted.org/packages/45/09/344d012dc91e60b8c7afee11ffae18338780c703a5b5fb32d8d82987e7cb/coverage-7.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f17055c50768d710d6abc789c9469d0353574780935e1381b83e63edc49ff530", size = 245313 }, + { url = "https://files.pythonhosted.org/packages/d2/2d/151b23e82aaea28aa7e3c0390d893bd1aef685866132aad36034f7d462b8/coverage-7.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:298d2917a6bfadbb272e08545ed026af3965e4d2fe71e3f38bf0a816818b226e", size = 243495 }, + { url = "https://files.pythonhosted.org/packages/74/5c/0da7fd4ad44259b4b61bd429dc642c6511314a356ffa782b924bd1ea9e5c/coverage-7.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d9be5d26e5f817d478506e4d3c4ff7b92f17d980670b4791bf05baaa37ce2f88", size = 244727 }, + { url = "https://files.pythonhosted.org/packages/de/08/6ccf2847c5c0d8fcc153bd8f4341d89ab50c85e01a15cabe4a546d3e943e/coverage-7.9.0-cp312-cp312-win32.whl", hash = "sha256:dc2784edd9ac9fe8692fc5505667deb0b05d895c016aaaf641031ed4a5f93d53", size = 214636 }, + { url = "https://files.pythonhosted.org/packages/79/fa/ae2c14d49475215372772f7638c333deaaacda8f3c5717a75377d1992c82/coverage-7.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:18223198464a6d5549db1934cf77a15deb24bb88652c4f5f7cb21cd3ad853704", size = 215448 }, + { url = "https://files.pythonhosted.org/packages/62/a9/45309219ba08b89cae84b2cb4ccfed8f941850aa7721c4914282fb3c1081/coverage-7.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:3b00194ff3c84d4b821822ff6c041f245fc55d0d5c7833fc4311d082e97595e8", size = 213817 }, + { url = "https://files.pythonhosted.org/packages/0b/59/449eb05f795d0050007b57a4efee79b540fa6fcccad813a191351964a001/coverage-7.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:122c60e92ab66c9c88e17565f67a91b3b3be5617cb50f73cfd34a4c60ed4aab0", size = 212271 }, + { url = "https://files.pythonhosted.org/packages/e0/3b/26852a4fb719a6007b0169c1b52116ed14b61267f0bf3ba1e23db516f352/coverage-7.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:813c11b367a6b3cf37212ec36b230f8d086c22b69dbf62877b40939fb2c79e74", size = 212538 }, + { url = "https://files.pythonhosted.org/packages/f6/80/99f82896119f36984a5b9189e71c7310fc036613276560b5884b5ee890d7/coverage-7.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f05e0f5e87f23d43fefe49e86655c6209dd4f9f034786b983e6803cf4554183", size = 245705 }, + { url = "https://files.pythonhosted.org/packages/a9/29/0b007deb096dd527c42e933129a8e4d5f9f1026f4953979c3a1e60e7ea9f/coverage-7.9.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62f465886fa4f86d5515da525aead97c5dff13a5cf997fc4c5097a1a59e063b2", size = 242918 }, + { url = "https://files.pythonhosted.org/packages/6f/eb/273855b57c7fb387dd9787f250b8b333ba8c1c100877c21e32eb1b24ff29/coverage-7.9.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:549ea4ca901595bbe3270e1afdef98bf5d4d5791596efbdc90b00449a2bb1f91", size = 244902 }, + { url = "https://files.pythonhosted.org/packages/20/57/4e411b47dbfd831538ecf9e5f407e42888b0c56aedbfe0ea7b102a787559/coverage-7.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:8cae1d4450945c74a6a65a09864ed3eaa917055cf70aa65f83ac1b9b0d8d5f9a", size = 245069 }, + { url = "https://files.pythonhosted.org/packages/91/75/b24cf5703fb325fc4b1899d89984dac117b99e757b9fadd525cad7ecc020/coverage-7.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d7b263910234c0d5ec913ec79ca921152fe874b805a7bcaf67118ef71708e5d2", size = 243040 }, + { url = "https://files.pythonhosted.org/packages/c7/e1/9495751d5315c3d76ee2c7b5dbc1935ab891d45ad585e1910a333dbdef43/coverage-7.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7d7b7425215963da8f5968096a20c5b5c9af4a86a950fcc25dcc2177ab33e9e5", size = 244424 }, + { url = "https://files.pythonhosted.org/packages/94/2a/ee504188a586da2379939f37fdc69047d9c46d35c34d1196f2605974a17d/coverage-7.9.0-cp313-cp313-win32.whl", hash = "sha256:e7dcfa92867b0c53d2e22e985c66af946dc09e8bb13c556709e396e90a0adf5c", size = 214677 }, + { url = "https://files.pythonhosted.org/packages/80/2b/5eab6518643c7560fe180ba5e0f35a0be3d4fc0a88aa6601120407b1fd03/coverage-7.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:aa34ca040785a2b768da489df0c036364d47a6c1c00bdd8f662b98fd3277d3d4", size = 215482 }, + { url = "https://files.pythonhosted.org/packages/fd/7f/9c9c8b736c4f40d7247bea8339afac40d8f6465491440608b3d73c10ffce/coverage-7.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:9c5dcb5cd3c52d84c5f52045e1c87c16bf189c2fbfa57cc0d811a3b4059939df", size = 213852 }, + { url = "https://files.pythonhosted.org/packages/e5/83/056464aec8b360dee6f4d7a517dc5ae5a9f462ff895ff536588b42f95b2d/coverage-7.9.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b52d2fdc1940f90c4572bd48211475a7b102f75a7f9a5e6cfc6e3da7dc380c44", size = 212994 }, + { url = "https://files.pythonhosted.org/packages/a3/87/f0291ecaa6baaaedbd428cf8b7e1d16b5dc010718fe7739cce955149ef83/coverage-7.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4cc555a3e6ceb8841df01a4634374f5f9635e661f5c307da00bce19819e8bcdf", size = 213212 }, + { url = "https://files.pythonhosted.org/packages/16/a0/9eb39541774a5beb662dc4ae98fee23afb947414b6aa1443b53d2ad3ea05/coverage-7.9.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:244f613617876b7cd32a097788d49c952a8f1698afb25275b2a825a4e895854e", size = 256453 }, + { url = "https://files.pythonhosted.org/packages/93/33/d0e99f4c809334dfed20f17234080a9003a713ddb80e33ad22697a8aa8e5/coverage-7.9.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5c335d77539e66bc6f83e8f1ef207d038129d9b9acd9dc9f0ca42fa9eedf564a", size = 252674 }, + { url = "https://files.pythonhosted.org/packages/0b/3a/d2a64e7ee5eb783e44e6ca404f8fc2a45afef052ed6593afb4ce9663dae6/coverage-7.9.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b335c7077c8da7bb8173d4f9ebd90ff1a97af6a6bec4fc4e6db4856ae80b31e", size = 254830 }, + { url = "https://files.pythonhosted.org/packages/e2/6a/9de640f8e2b097d155532d1bc16eb9c5186fccc7c4b8148fe1dd2520875a/coverage-7.9.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:01cbc2c36895b7ab906514042c92b3fc9dd0526bf1c3251cb6aefd9c71ae6dda", size = 256060 }, + { url = "https://files.pythonhosted.org/packages/07/72/928fa3583b9783fc32e3dfafb6cc0cf73bdd73d1dc41e3a973f203c6aeff/coverage-7.9.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1ac62880a9dff0726a193ce77a1bcdd4e8491009cb3a0510d31381e8b2c46d7a", size = 254174 }, + { url = "https://files.pythonhosted.org/packages/ad/01/2fd0785f8768693b748e36b442352bc26edf3391246eedcc80d480d06da1/coverage-7.9.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:95314eb306cf54af3d1147e27ba008cf78eed6f1309a1310772f4f05b12c9c65", size = 255011 }, + { url = "https://files.pythonhosted.org/packages/b7/49/1d0120cfa24e001e0d38795388914183c48cd86fc8640ca3b01337831917/coverage-7.9.0-cp313-cp313t-win32.whl", hash = "sha256:c5cbf3ddfb68de8dc8ce33caa9321df27297a032aeaf2e99b278f183fb4ebc37", size = 215349 }, + { url = "https://files.pythonhosted.org/packages/9f/48/7625c09621a206fff0b51fcbcf5d6c1162ab10a5ffa546fc132f01c9132b/coverage-7.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:e3ec9e1525eb7a0f89d31083539b398d921415d884e9f55400002a1e9fe0cf63", size = 216516 }, + { url = "https://files.pythonhosted.org/packages/bb/50/048b55c34985c3aafcecb32cced3abc4291969bfd967dbcaed95cfc26b2a/coverage-7.9.0-cp313-cp313t-win_arm64.whl", hash = "sha256:a02efe6769f74245ce476e89db3d4e110db07b4c0c3d3f81728e2464bbbbcb8e", size = 214308 }, + { url = "https://files.pythonhosted.org/packages/c3/4e/4c72909d117d593e388c82b8bc29f99ad0fe20fe84f6390ee14d5650b750/coverage-7.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:64dab59d812c1cbfc9cebadada377365874964acdf59b12e86487d25c2e0c29f", size = 211938 }, + { url = "https://files.pythonhosted.org/packages/84/84/8e2e1ebe02a5c68c4ac54668392ee00fa5ea8e7989b339d847fff27220bd/coverage-7.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46b9dc640c6309fb49625d3569d4ba7abe2afcba645eb1e52bad97510f60ac26", size = 212314 }, + { url = "https://files.pythonhosted.org/packages/1c/61/931117485d6917f4719be2bf8cc25c79c7108c078b005b38882688e1f41b/coverage-7.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89358f4025ed424861311b33815a2866f7c94856c932b0ffc98180f655e813e2", size = 241077 }, + { url = "https://files.pythonhosted.org/packages/f9/58/431fbfb00a4dfc1d845b70d296b503d306be76d07a67a4046b15e42c8234/coverage-7.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:589e37ae75d81fd53cd1ca624e07af4466e9e4ce259e3bfe2b147896857c06ea", size = 238945 }, + { url = "https://files.pythonhosted.org/packages/a5/e2/8b2cc9b761bee876472379db92d017d7042eeaddba35adf67f54e3ceff3d/coverage-7.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29dea81eef5432076cee561329b3831bc988a4ce1bfaec90eee2078ff5311e6e", size = 240063 }, + { url = "https://files.pythonhosted.org/packages/6c/39/e1b0ba8cac5ae66a13475cb08b184f06d89515b6ea6ed45cd678ae2fbcb1/coverage-7.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7b3482588772b6b24601d1677aef299af28d6c212c70b0be27bdfc2e10fb00fe", size = 239789 }, + { url = "https://files.pythonhosted.org/packages/6e/91/b6b926cd875cd03989abb696ccbbd5895e367e6394dcf7c264180f72d038/coverage-7.9.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2debc0b9481b5fc76f771b3b31e89a0cd8791ad977654940a3523f3f2e5d98fe", size = 238041 }, + { url = "https://files.pythonhosted.org/packages/43/ce/de736582c44906b5d6067b650ac851d5f249e246753b9d8f7369e7eea00a/coverage-7.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:304ded640bc2a60f14a2ff0fec98cce4c3f2e573c122f0548728c8dceba5abe7", size = 238977 }, + { url = "https://files.pythonhosted.org/packages/27/d3/35317997155b16b140a2c62f09e001a12e244b2d410deb5b8cfa861173f4/coverage-7.9.0-cp39-cp39-win32.whl", hash = "sha256:8e0a3a3f9b968007e1f56418a3586f9a983c84ac4e84d28d1c4f8b76c4226282", size = 214442 }, + { url = "https://files.pythonhosted.org/packages/0a/42/d4bcd2900c05bdb5773d47173395c68c147b4ca2564e791c8c9b0ed42c73/coverage-7.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:cb3c07dd71d1ff52156d35ee6fa48458c3cec1add7fcce6a934f977fb80c48a5", size = 215351 }, + { url = "https://files.pythonhosted.org/packages/e8/b6/d16966f9439ccc3007e1740960d241420d6ba81502642a4be1da1672a103/coverage-7.9.0-pp39.pp310.pp311-none-any.whl", hash = "sha256:ccf1540a0e82ff525844880f988f6caaa2d037005e57bfe203b71cac7626145d", size = 203927 }, + { url = "https://files.pythonhosted.org/packages/70/0d/534c1e35cb7688b5c40de93fcca07e3ddc0287659ff85cd376b1dd3f770f/coverage-7.9.0-py3-none-any.whl", hash = "sha256:79ea9a26b27c963cdf541e1eb9ac05311b012bc367d0e31816f1833b06c81c02", size = 203917 }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "python_full_version <= '3.11'" }, +] + +[[package]] +name = "editor" +version = "1.6.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "runs" }, + { name = "xmod" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/92/734a4ab345914259cb6146fd36512608ea42be16195375c379046f33283d/editor-1.6.6.tar.gz", hash = "sha256:bb6989e872638cd119db9a4fce284cd8e13c553886a1c044c6b8d8a160c871f8", size = 3197 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/c2/4bc8cd09b14e28ce3f406a8b05761bed0d785d1ca8c2a5c6684d884c66a2/editor-1.6.6-py3-none-any.whl", hash = "sha256:e818e6913f26c2a81eadef503a2741d7cca7f235d20e217274a009ecd5a74abf", size = 4017 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674 }, +] + +[[package]] +name = "faker" +version = "37.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/97/4b/5354912eaff922876323f2d07e21408b10867f3295d5f917748341cb6f53/faker-37.3.0.tar.gz", hash = "sha256:77b79e7a2228d57175133af0bbcdd26dc623df81db390ee52f5104d46c010f2f", size = 1901376 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/99/045b2dae19a01b9fbb23b9971bc04f4ef808e7f3a213d08c81067304a210/faker-37.3.0-py3-none-any.whl", hash = "sha256:48c94daa16a432f2d2bc803c7ff602509699fca228d13e97e379cd860a7e216e", size = 1942203 }, +] + +[[package]] +name = "httpretty" +version = "1.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/19/850b7ed736319d0c4088581f4fc34f707ef14461947284026664641e16d4/httpretty-1.1.4.tar.gz", hash = "sha256:20de0e5dd5a18292d36d928cc3d6e52f8b2ac73daec40d41eb62dee154933b68", size = 442389 } + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461 }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 }, +] + +[[package]] +name = "inquirer" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "blessed" }, + { name = "editor" }, + { name = "readchar" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/06/ef91eb8f3feafb736aa33dcb278fc9555d17861aa571b684715d095db24d/inquirer-3.4.0.tar.gz", hash = "sha256:8edc99c076386ee2d2204e5e3653c2488244e82cb197b2d498b3c1b5ffb25d0b", size = 14472 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/b2/be907c8c0f8303bc4b10089f5470014c3bf3521e9b8d3decf3037fd94725/inquirer-3.4.0-py3-none-any.whl", hash = "sha256:bb0ec93c833e4ce7b51b98b1644b0a4d2bb39755c39787f6a504e4fee7a11b60", size = 18077 }, +] + +[[package]] +name = "isodate" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jinxed" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ansicon", marker = "platform_system == 'Windows'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/d0/59b2b80e7a52d255f9e0ad040d2e826342d05580c4b1d7d7747cfb8db731/jinxed-1.3.0.tar.gz", hash = "sha256:1593124b18a41b7a3da3b078471442e51dbad3d77b4d4f2b0c26ab6f7d660dbf", size = 80981 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/e3/0e0014d6ab159d48189e92044ace13b1e1fe9aa3024ba9f4e8cf172aa7c2/jinxed-1.3.0-py2.py3-none-any.whl", hash = "sha256:b993189f39dc2d7504d802152671535b06d380b26d78070559551cbf92df4fc5", size = 33085 }, +] + +[[package]] +name = "jsonschema" +version = "4.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709 }, +] + +[[package]] +name = "jsonschema-path" +version = "0.3.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pathable" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2025.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437 }, +] + +[[package]] +name = "lazy-object-proxy" +version = "1.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/57/f9/1f56571ed82fb324f293661690635cf42c41deb8a70a6c9e6edc3e9bb3c8/lazy_object_proxy-1.11.0.tar.gz", hash = "sha256:18874411864c9fbbbaa47f9fc1dd7aea754c86cfde21278ef427639d1dd78e9c", size = 44736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/c8/457f1555f066f5bacc44337141294153dc993b5e9132272ab54a64ee98a2/lazy_object_proxy-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:132bc8a34f2f2d662a851acfd1b93df769992ed1b81e2b1fda7db3e73b0d5a18", size = 28045 }, + { url = "https://files.pythonhosted.org/packages/18/33/3260b4f8de6f0942008479fee6950b2b40af11fc37dba23aa3672b0ce8a6/lazy_object_proxy-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:01261a3afd8621a1accb5682df2593dc7ec7d21d38f411011a5712dcd418fbed", size = 28441 }, + { url = "https://files.pythonhosted.org/packages/51/f6/eb645ca1ff7408bb69e9b1fe692cce1d74394efdbb40d6207096c0cd8381/lazy_object_proxy-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:090935756cc041e191f22f4f9c7fd4fe9a454717067adf5b1bbd2ce3046b556e", size = 28047 }, + { url = "https://files.pythonhosted.org/packages/13/9c/aabbe1e8b99b8b0edb846b49a517edd636355ac97364419d9ba05b8fa19f/lazy_object_proxy-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:76ec715017f06410f57df442c1a8d66e6b5f7035077785b129817f5ae58810a4", size = 28440 }, + { url = "https://files.pythonhosted.org/packages/4d/24/dae4759469e9cd318fef145f7cfac7318261b47b23a4701aa477b0c3b42c/lazy_object_proxy-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a9f39098e93a63618a79eef2889ae3cf0605f676cd4797fdfd49fcd7ddc318b", size = 28142 }, + { url = "https://files.pythonhosted.org/packages/de/0c/645a881f5f27952a02f24584d96f9f326748be06ded2cee25f8f8d1cd196/lazy_object_proxy-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ee13f67f4fcd044ef27bfccb1c93d39c100046fec1fad6e9a1fcdfd17492aeb3", size = 28380 }, + { url = "https://files.pythonhosted.org/packages/a8/0f/6e004f928f7ff5abae2b8e1f68835a3870252f886e006267702e1efc5c7b/lazy_object_proxy-1.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4c84eafd8dd15ea16f7d580758bc5c2ce1f752faec877bb2b1f9f827c329cd", size = 28149 }, + { url = "https://files.pythonhosted.org/packages/63/cb/b8363110e32cc1fd82dc91296315f775d37a39df1c1cfa976ec1803dac89/lazy_object_proxy-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:d2503427bda552d3aefcac92f81d9e7ca631e680a2268cbe62cd6a58de6409b7", size = 28389 }, + { url = "https://files.pythonhosted.org/packages/7b/89/68c50fcfd81e11480cd8ee7f654c9bd790a9053b9a0efe9983d46106f6a9/lazy_object_proxy-1.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0613116156801ab3fccb9e2b05ed83b08ea08c2517fdc6c6bc0d4697a1a376e3", size = 28777 }, + { url = "https://files.pythonhosted.org/packages/39/d0/7e967689e24de8ea6368ec33295f9abc94b9f3f0cd4571bfe148dc432190/lazy_object_proxy-1.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bb03c507d96b65f617a6337dedd604399d35face2cdf01526b913fb50c4cb6e8", size = 29598 }, + { url = "https://files.pythonhosted.org/packages/04/61/24ad1506df7edc9f8fa396fd5bbe66bdfb41c1f3d131a04802fb1a48615b/lazy_object_proxy-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28c174db37946f94b97a97b579932ff88f07b8d73a46b6b93322b9ac06794a3b", size = 28041 }, + { url = "https://files.pythonhosted.org/packages/a3/be/5d7a93ad2892584c7aaad3db78fd5b4c14020d6f076b1f736825b7bbe2b3/lazy_object_proxy-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:d662f0669e27704495ff1f647070eb8816931231c44e583f4d0701b7adf6272f", size = 28307 }, + { url = "https://files.pythonhosted.org/packages/e7/1e/fb441c07b6662ec1fc92b249225ba6e6e5221b05623cb0131d082f782edc/lazy_object_proxy-1.11.0-py3-none-any.whl", hash = "sha256:a56a5093d433341ff7da0e89f9b486031ccd222ec8e52ec84d0ec1cdc819674b", size = 16635 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/90/d08277ce111dd22f77149fd1a5d4653eeb3b3eaacbdfcbae5afb2600eebd/MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", size = 14357 }, + { url = "https://files.pythonhosted.org/packages/04/e1/6e2194baeae0bca1fae6629dc0cbbb968d4d941469cbab11a3872edff374/MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", size = 12393 }, + { url = "https://files.pythonhosted.org/packages/1d/69/35fa85a8ece0a437493dc61ce0bb6d459dcba482c34197e3efc829aa357f/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", size = 21732 }, + { url = "https://files.pythonhosted.org/packages/22/35/137da042dfb4720b638d2937c38a9c2df83fe32d20e8c8f3185dbfef05f7/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", size = 20866 }, + { url = "https://files.pythonhosted.org/packages/29/28/6d029a903727a1b62edb51863232152fd335d602def598dade38996887f0/MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", size = 20964 }, + { url = "https://files.pythonhosted.org/packages/cc/cd/07438f95f83e8bc028279909d9c9bd39e24149b0d60053a97b2bc4f8aa51/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", size = 21977 }, + { url = "https://files.pythonhosted.org/packages/29/01/84b57395b4cc062f9c4c55ce0df7d3108ca32397299d9df00fedd9117d3d/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", size = 21366 }, + { url = "https://files.pythonhosted.org/packages/bd/6e/61ebf08d8940553afff20d1fb1ba7294b6f8d279df9fd0c0db911b4bbcfd/MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", size = 21091 }, + { url = "https://files.pythonhosted.org/packages/11/23/ffbf53694e8c94ebd1e7e491de185124277964344733c45481f32ede2499/MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50", size = 15065 }, + { url = "https://files.pythonhosted.org/packages/44/06/e7175d06dd6e9172d4a69a72592cb3f7a996a9c396eee29082826449bbc3/MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", size = 15514 }, + { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, + { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, + { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, + { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, + { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, + { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, + { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, + { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, + { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, + { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, + { url = "https://files.pythonhosted.org/packages/a7/ea/9b1530c3fdeeca613faeb0fb5cbcf2389d816072fab72a71b45749ef6062/MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", size = 14344 }, + { url = "https://files.pythonhosted.org/packages/4b/c2/fbdbfe48848e7112ab05e627e718e854d20192b674952d9042ebd8c9e5de/MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", size = 12389 }, + { url = "https://files.pythonhosted.org/packages/f0/25/7a7c6e4dbd4f867d95d94ca15449e91e52856f6ed1905d58ef1de5e211d0/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", size = 21607 }, + { url = "https://files.pythonhosted.org/packages/53/8f/f339c98a178f3c1e545622206b40986a4c3307fe39f70ccd3d9df9a9e425/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", size = 20728 }, + { url = "https://files.pythonhosted.org/packages/1a/03/8496a1a78308456dbd50b23a385c69b41f2e9661c67ea1329849a598a8f9/MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", size = 20826 }, + { url = "https://files.pythonhosted.org/packages/e6/cf/0a490a4bd363048c3022f2f475c8c05582179bb179defcee4766fb3dcc18/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", size = 21843 }, + { url = "https://files.pythonhosted.org/packages/19/a3/34187a78613920dfd3cdf68ef6ce5e99c4f3417f035694074beb8848cd77/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", size = 21219 }, + { url = "https://files.pythonhosted.org/packages/17/d8/5811082f85bb88410ad7e452263af048d685669bbbfb7b595e8689152498/MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", size = 20946 }, + { url = "https://files.pythonhosted.org/packages/7c/31/bd635fb5989440d9365c5e3c47556cfea121c7803f5034ac843e8f37c2f2/MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", size = 15063 }, + { url = "https://files.pythonhosted.org/packages/b3/73/085399401383ce949f727afec55ec3abd76648d04b9f22e1c0e99cb4bec3/MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", size = 15506 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "more-itertools" +version = "10.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/a0/834b0cebabbfc7e311f30b46c8188790a37f89fc8d756660346fe5abfd09/more_itertools-10.7.0.tar.gz", hash = "sha256:9fddd5403be01a94b204faadcff459ec3568cf110265d3c54323e1e866ad29d3", size = 127671 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/9f/7ba6f94fc1e9ac3d2b853fdff3035fb2fa5afbed898c4a72b8a020610594/more_itertools-10.7.0-py3-none-any.whl", hash = "sha256:d43980384673cb07d2f7d2d918c616b30c659c089ee23953f601d6609c67510e", size = 65278 }, +] + +[[package]] +name = "openapi-core" +version = "0.19.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "isodate" }, + { name = "jsonschema" }, + { name = "jsonschema-path" }, + { name = "more-itertools" }, + { name = "openapi-schema-validator" }, + { name = "openapi-spec-validator" }, + { name = "parse" }, + { name = "typing-extensions" }, + { name = "werkzeug" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/35/1acaa5f2fcc6e54eded34a2ec74b479439c4e469fc4e8d0e803fda0234db/openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3", size = 103264 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/6f/83ead0e2e30a90445ee4fc0135f43741aebc30cca5b43f20968b603e30b6/openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f", size = 106595 }, +] + +[[package]] +name = "openapi-schema-validator" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-specifications" }, + { name = "rfc3339-validator" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/5507ad3325169347cd8ced61c232ff3df70e2b250c49f0fe140edb4973c6/openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee", size = 11550 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/c6/ad0fba32775ae749016829dace42ed80f4407b171da41313d1a3a5f102e4/openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3", size = 8755 }, +] + +[[package]] +name = "openapi-spec-validator" +version = "0.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jsonschema" }, + { name = "jsonschema-path" }, + { name = "lazy-object-proxy" }, + { name = "openapi-schema-validator" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/fe/21954ff978239dc29ebb313f5c87eeb4ec929b694b9667323086730998e2/openapi_spec_validator-0.7.1.tar.gz", hash = "sha256:8577b85a8268685da6f8aa30990b83b7960d4d1117e901d451b5d572605e5ec7", size = 37985 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/4d/e744fff95aaf3aeafc968d5ba7297c8cda0d1ecb8e3acd21b25adae4d835/openapi_spec_validator-0.7.1-py3-none-any.whl", hash = "sha256:3c81825043f24ccbcd2f4b149b11e8231abce5ba84f37065e14ec947d8f4e959", size = 38998 }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 }, +] + +[[package]] +name = "parse" +version = "1.20.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/78/d9b09ba24bb36ef8b83b71be547e118d46214735b6dfb39e4bfde0e9b9dd/parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce", size = 29391 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126 }, +] + +[[package]] +name = "pathable" +version = "0.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592 }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538 }, +] + +[[package]] +name = "pygments" +version = "2.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, +] + +[[package]] +name = "pytest" +version = "8.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fb/aa/405082ce2749be5398045152251ac69c0f3578c7077efc53431303af97ce/pytest-8.4.0.tar.gz", hash = "sha256:14d920b48472ea0dbf68e45b96cd1ffda4705f33307dcc86c676c1b5104838a6", size = 1515232 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797 }, +] + +[[package]] +name = "pytest-cov" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"] }, + { name = "pluggy" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/99/668cade231f434aaa59bbfbf49469068d2ddd945000621d3d165d2e7dd7b/pytest_cov-6.2.1.tar.gz", hash = "sha256:25cc6cc0a5358204b8108ecedc51a9b57b34cc6b8c967cc2c01a4e00d8a67da2", size = 69432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644 }, +] + +[[package]] +name = "python-dialpad" +version = "3.0.0" +source = { editable = "." } +dependencies = [ + { name = "cached-property" }, + { name = "requests" }, +] + +[package.dev-dependencies] +dev = [ + { name = "faker" }, + { name = "inquirer" }, + { name = "openapi-core" }, + { name = "pytest" }, + { name = "pytest-cov" }, + { name = "requests-mock" }, + { name = "ruff" }, + { name = "six" }, + { name = "swagger-parser" }, + { name = "swagger-stub" }, + { name = "typer" }, + { name = "urllib3" }, +] + +[package.metadata] +requires-dist = [ + { name = "cached-property", specifier = ">=2.0.1" }, + { name = "requests", specifier = ">=2.28.0" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "faker", specifier = ">=37.3.0" }, + { name = "inquirer", specifier = ">=3.4.0" }, + { name = "openapi-core", specifier = ">=0.19.5" }, + { name = "pytest", specifier = ">=8.4.0" }, + { name = "pytest-cov", specifier = ">=6.2.1" }, + { name = "requests-mock", specifier = ">=1.12.1" }, + { name = "ruff", specifier = ">=0.11.12" }, + { name = "six", specifier = ">=1.17.0" }, + { name = "swagger-parser", git = "https://github.com/jakedialpad/swagger-parser?rev=v1.0.1b" }, + { name = "swagger-stub", specifier = ">=0.2.1" }, + { name = "typer", specifier = ">=0.16.0" }, + { name = "urllib3", specifier = ">=2.4.0" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "readchar" +version = "4.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/f8/8657b8cbb4ebeabfbdf991ac40eca8a1d1bd012011bd44ad1ed10f5cb494/readchar-4.2.1.tar.gz", hash = "sha256:91ce3faf07688de14d800592951e5575e9c7a3213738ed01d394dcc949b79adb", size = 9685 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/10/e4b1e0e5b6b6745c8098c275b69bc9d73e9542d5c7da4f137542b499ed44/readchar-4.2.1-py3-none-any.whl", hash = "sha256:a769305cd3994bb5fa2764aa4073452dc105a4ec39068ffe6efd3c20c60acc77", size = 9350 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "requests-mock" +version = "1.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/92/32/587625f91f9a0a3d84688bf9cfc4b2480a7e8ec327cefd0ff2ac891fd2cf/requests-mock-1.12.1.tar.gz", hash = "sha256:e9e12e333b525156e82a3c852f22016b9158220d2f47454de9cae8a77d371401", size = 60901 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/ec/889fbc557727da0c34a33850950310240f2040f3b1955175fdb2b36a8910/requests_mock-1.12.1-py2.py3-none-any.whl", hash = "sha256:b1e37054004cdd5e56c84454cc7df12b25f90f382159087f4b6915aaeef39563", size = 27695 }, +] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, +] + +[[package]] +name = "rich" +version = "14.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 }, +] + +[[package]] +name = "rpds-py" +version = "0.25.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/a6/60184b7fc00dd3ca80ac635dd5b8577d444c57e8e8742cecabfacb829921/rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", size = 27304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/09/e1158988e50905b7f8306487a576b52d32aa9a87f79f7ab24ee8db8b6c05/rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", size = 373140 }, + { url = "https://files.pythonhosted.org/packages/e0/4b/a284321fb3c45c02fc74187171504702b2934bfe16abab89713eedfe672e/rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", size = 358860 }, + { url = "https://files.pythonhosted.org/packages/4e/46/8ac9811150c75edeae9fc6fa0e70376c19bc80f8e1f7716981433905912b/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", size = 386179 }, + { url = "https://files.pythonhosted.org/packages/f3/ec/87eb42d83e859bce91dcf763eb9f2ab117142a49c9c3d17285440edb5b69/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", size = 400282 }, + { url = "https://files.pythonhosted.org/packages/68/c8/2a38e0707d7919c8c78e1d582ab15cf1255b380bcb086ca265b73ed6db23/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", size = 521824 }, + { url = "https://files.pythonhosted.org/packages/5e/2c/6a92790243569784dde84d144bfd12bd45102f4a1c897d76375076d730ab/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", size = 411644 }, + { url = "https://files.pythonhosted.org/packages/eb/76/66b523ffc84cf47db56efe13ae7cf368dee2bacdec9d89b9baca5e2e6301/rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", size = 386955 }, + { url = "https://files.pythonhosted.org/packages/b6/b9/a362d7522feaa24dc2b79847c6175daa1c642817f4a19dcd5c91d3e2c316/rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", size = 421039 }, + { url = "https://files.pythonhosted.org/packages/0f/c4/b5b6f70b4d719b6584716889fd3413102acf9729540ee76708d56a76fa97/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", size = 563290 }, + { url = "https://files.pythonhosted.org/packages/87/a3/2e6e816615c12a8f8662c9d8583a12eb54c52557521ef218cbe3095a8afa/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", size = 592089 }, + { url = "https://files.pythonhosted.org/packages/c0/08/9b8e1050e36ce266135994e2c7ec06e1841f1c64da739daeb8afe9cb77a4/rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", size = 558400 }, + { url = "https://files.pythonhosted.org/packages/f2/df/b40b8215560b8584baccd839ff5c1056f3c57120d79ac41bd26df196da7e/rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", size = 219741 }, + { url = "https://files.pythonhosted.org/packages/10/99/e4c58be18cf5d8b40b8acb4122bc895486230b08f978831b16a3916bd24d/rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", size = 231553 }, + { url = "https://files.pythonhosted.org/packages/95/e1/df13fe3ddbbea43567e07437f097863b20c99318ae1f58a0fe389f763738/rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", size = 373341 }, + { url = "https://files.pythonhosted.org/packages/7a/58/deef4d30fcbcbfef3b6d82d17c64490d5c94585a2310544ce8e2d3024f83/rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", size = 359111 }, + { url = "https://files.pythonhosted.org/packages/bb/7e/39f1f4431b03e96ebaf159e29a0f82a77259d8f38b2dd474721eb3a8ac9b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", size = 386112 }, + { url = "https://files.pythonhosted.org/packages/db/e7/847068a48d63aec2ae695a1646089620b3b03f8ccf9f02c122ebaf778f3c/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", size = 400362 }, + { url = "https://files.pythonhosted.org/packages/3b/3d/9441d5db4343d0cee759a7ab4d67420a476cebb032081763de934719727b/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", size = 522214 }, + { url = "https://files.pythonhosted.org/packages/a2/ec/2cc5b30d95f9f1a432c79c7a2f65d85e52812a8f6cbf8768724571710786/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", size = 411491 }, + { url = "https://files.pythonhosted.org/packages/dc/6c/44695c1f035077a017dd472b6a3253553780837af2fac9b6ac25f6a5cb4d/rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", size = 386978 }, + { url = "https://files.pythonhosted.org/packages/b1/74/b4357090bb1096db5392157b4e7ed8bb2417dc7799200fcbaee633a032c9/rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", size = 420662 }, + { url = "https://files.pythonhosted.org/packages/26/dd/8cadbebf47b96e59dfe8b35868e5c38a42272699324e95ed522da09d3a40/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", size = 563385 }, + { url = "https://files.pythonhosted.org/packages/c3/ea/92960bb7f0e7a57a5ab233662f12152085c7dc0d5468534c65991a3d48c9/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", size = 592047 }, + { url = "https://files.pythonhosted.org/packages/61/ad/71aabc93df0d05dabcb4b0c749277881f8e74548582d96aa1bf24379493a/rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", size = 557863 }, + { url = "https://files.pythonhosted.org/packages/93/0f/89df0067c41f122b90b76f3660028a466eb287cbe38efec3ea70e637ca78/rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", size = 219627 }, + { url = "https://files.pythonhosted.org/packages/7c/8d/93b1a4c1baa903d0229374d9e7aa3466d751f1d65e268c52e6039c6e338e/rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", size = 231603 }, + { url = "https://files.pythonhosted.org/packages/cb/11/392605e5247bead2f23e6888e77229fbd714ac241ebbebb39a1e822c8815/rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", size = 223967 }, + { url = "https://files.pythonhosted.org/packages/7f/81/28ab0408391b1dc57393653b6a0cf2014cc282cc2909e4615e63e58262be/rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", size = 364647 }, + { url = "https://files.pythonhosted.org/packages/2c/9a/7797f04cad0d5e56310e1238434f71fc6939d0bc517192a18bb99a72a95f/rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", size = 350454 }, + { url = "https://files.pythonhosted.org/packages/69/3c/93d2ef941b04898011e5d6eaa56a1acf46a3b4c9f4b3ad1bbcbafa0bee1f/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", size = 389665 }, + { url = "https://files.pythonhosted.org/packages/c1/57/ad0e31e928751dde8903a11102559628d24173428a0f85e25e187defb2c1/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", size = 403873 }, + { url = "https://files.pythonhosted.org/packages/16/ad/c0c652fa9bba778b4f54980a02962748479dc09632e1fd34e5282cf2556c/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", size = 525866 }, + { url = "https://files.pythonhosted.org/packages/2a/39/3e1839bc527e6fcf48d5fec4770070f872cdee6c6fbc9b259932f4e88a38/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", size = 416886 }, + { url = "https://files.pythonhosted.org/packages/7a/95/dd6b91cd4560da41df9d7030a038298a67d24f8ca38e150562644c829c48/rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", size = 390666 }, + { url = "https://files.pythonhosted.org/packages/64/48/1be88a820e7494ce0a15c2d390ccb7c52212370badabf128e6a7bb4cb802/rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", size = 425109 }, + { url = "https://files.pythonhosted.org/packages/cf/07/3e2a17927ef6d7720b9949ec1b37d1e963b829ad0387f7af18d923d5cfa5/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", size = 567244 }, + { url = "https://files.pythonhosted.org/packages/d2/e5/76cf010998deccc4f95305d827847e2eae9c568099c06b405cf96384762b/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", size = 596023 }, + { url = "https://files.pythonhosted.org/packages/52/9a/df55efd84403736ba37a5a6377b70aad0fd1cb469a9109ee8a1e21299a1c/rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", size = 561634 }, + { url = "https://files.pythonhosted.org/packages/ab/aa/dc3620dd8db84454aaf9374bd318f1aa02578bba5e567f5bf6b79492aca4/rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", size = 222713 }, + { url = "https://files.pythonhosted.org/packages/a3/7f/7cef485269a50ed5b4e9bae145f512d2a111ca638ae70cc101f661b4defd/rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", size = 235280 }, + { url = "https://files.pythonhosted.org/packages/99/f2/c2d64f6564f32af913bf5f3f7ae41c7c263c5ae4c4e8f1a17af8af66cd46/rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", size = 225399 }, + { url = "https://files.pythonhosted.org/packages/2b/da/323848a2b62abe6a0fec16ebe199dc6889c5d0a332458da8985b2980dffe/rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", size = 364498 }, + { url = "https://files.pythonhosted.org/packages/1f/b4/4d3820f731c80fd0cd823b3e95b9963fec681ae45ba35b5281a42382c67d/rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", size = 350083 }, + { url = "https://files.pythonhosted.org/packages/d5/b1/3a8ee1c9d480e8493619a437dec685d005f706b69253286f50f498cbdbcf/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", size = 389023 }, + { url = "https://files.pythonhosted.org/packages/3b/31/17293edcfc934dc62c3bf74a0cb449ecd549531f956b72287203e6880b87/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", size = 403283 }, + { url = "https://files.pythonhosted.org/packages/d1/ca/e0f0bc1a75a8925024f343258c8ecbd8828f8997ea2ac71e02f67b6f5299/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", size = 524634 }, + { url = "https://files.pythonhosted.org/packages/3e/03/5d0be919037178fff33a6672ffc0afa04ea1cfcb61afd4119d1b5280ff0f/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", size = 416233 }, + { url = "https://files.pythonhosted.org/packages/05/7c/8abb70f9017a231c6c961a8941403ed6557664c0913e1bf413cbdc039e75/rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", size = 390375 }, + { url = "https://files.pythonhosted.org/packages/7a/ac/a87f339f0e066b9535074a9f403b9313fd3892d4a164d5d5f5875ac9f29f/rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", size = 424537 }, + { url = "https://files.pythonhosted.org/packages/1f/8f/8d5c1567eaf8c8afe98a838dd24de5013ce6e8f53a01bd47fe8bb06b5533/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", size = 566425 }, + { url = "https://files.pythonhosted.org/packages/95/33/03016a6be5663b389c8ab0bbbcca68d9e96af14faeff0a04affcb587e776/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", size = 595197 }, + { url = "https://files.pythonhosted.org/packages/33/8d/da9f4d3e208c82fda311bff0cf0a19579afceb77cf456e46c559a1c075ba/rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", size = 561244 }, + { url = "https://files.pythonhosted.org/packages/e2/b3/39d5dcf7c5f742ecd6dbc88f6f84ae54184b92f5f387a4053be2107b17f1/rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", size = 222254 }, + { url = "https://files.pythonhosted.org/packages/5f/19/2d6772c8eeb8302c5f834e6d0dfd83935a884e7c5ce16340c7eaf89ce925/rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", size = 234741 }, + { url = "https://files.pythonhosted.org/packages/5b/5a/145ada26cfaf86018d0eb304fe55eafdd4f0b6b84530246bb4a7c4fb5c4b/rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", size = 224830 }, + { url = "https://files.pythonhosted.org/packages/4b/ca/d435844829c384fd2c22754ff65889c5c556a675d2ed9eb0e148435c6690/rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", size = 359668 }, + { url = "https://files.pythonhosted.org/packages/1f/01/b056f21db3a09f89410d493d2f6614d87bb162499f98b649d1dbd2a81988/rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", size = 345649 }, + { url = "https://files.pythonhosted.org/packages/e0/0f/e0d00dc991e3d40e03ca36383b44995126c36b3eafa0ccbbd19664709c88/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", size = 384776 }, + { url = "https://files.pythonhosted.org/packages/9f/a2/59374837f105f2ca79bde3c3cd1065b2f8c01678900924949f6392eab66d/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", size = 395131 }, + { url = "https://files.pythonhosted.org/packages/9c/dc/48e8d84887627a0fe0bac53f0b4631e90976fd5d35fff8be66b8e4f3916b/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", size = 520942 }, + { url = "https://files.pythonhosted.org/packages/7c/f5/ee056966aeae401913d37befeeab57a4a43a4f00099e0a20297f17b8f00c/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", size = 411330 }, + { url = "https://files.pythonhosted.org/packages/ab/74/b2cffb46a097cefe5d17f94ede7a174184b9d158a0aeb195f39f2c0361e8/rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", size = 387339 }, + { url = "https://files.pythonhosted.org/packages/7f/9a/0ff0b375dcb5161c2b7054e7d0b7575f1680127505945f5cabaac890bc07/rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", size = 418077 }, + { url = "https://files.pythonhosted.org/packages/0d/a1/fda629bf20d6b698ae84c7c840cfb0e9e4200f664fc96e1f456f00e4ad6e/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", size = 562441 }, + { url = "https://files.pythonhosted.org/packages/20/15/ce4b5257f654132f326f4acd87268e1006cc071e2c59794c5bdf4bebbb51/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", size = 590750 }, + { url = "https://files.pythonhosted.org/packages/fb/ab/e04bf58a8d375aeedb5268edcc835c6a660ebf79d4384d8e0889439448b0/rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", size = 558891 }, + { url = "https://files.pythonhosted.org/packages/90/82/cb8c6028a6ef6cd2b7991e2e4ced01c854b6236ecf51e81b64b569c43d73/rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", size = 218718 }, + { url = "https://files.pythonhosted.org/packages/b6/97/5a4b59697111c89477d20ba8a44df9ca16b41e737fa569d5ae8bff99e650/rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", size = 232218 }, + { url = "https://files.pythonhosted.org/packages/89/74/716d42058ef501e2c08f27aa3ff455f6fc1bbbd19a6ab8dea07e6322d217/rpds_py-0.25.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd", size = 373475 }, + { url = "https://files.pythonhosted.org/packages/e1/21/3faa9c523e2496a2505d7440b6f24c9166f37cb7ac027cac6cfbda9b4b5f/rpds_py-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634", size = 359349 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/c747fe568d21b1d679079b52b926ebc4d1497457510a1773dc5fd4b7b4e2/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be", size = 386526 }, + { url = "https://files.pythonhosted.org/packages/0b/cc/4a41703de4fb291f13660fa3d882cbd39db5d60497c6e7fa7f5142e5e69f/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0", size = 400526 }, + { url = "https://files.pythonhosted.org/packages/f1/78/60c980bedcad8418b614f0b4d6d420ecf11225b579cec0cb4e84d168b4da/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908", size = 525726 }, + { url = "https://files.pythonhosted.org/packages/3f/37/f2f36b7f1314b3c3200d663decf2f8e29480492a39ab22447112aead4693/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a", size = 412045 }, + { url = "https://files.pythonhosted.org/packages/df/96/e03783e87a775b1242477ccbc35895f8e9b2bbdb60e199034a6da03c2687/rpds_py-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9", size = 386953 }, + { url = "https://files.pythonhosted.org/packages/7c/7d/1418f4b69bfb4b40481a3d84782113ad7d4cca0b38ae70b982dd5b20102a/rpds_py-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80", size = 421144 }, + { url = "https://files.pythonhosted.org/packages/b3/0e/61469912c6493ee3808012e60f4930344b974fcb6b35c4348e70b6be7bc7/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a", size = 563730 }, + { url = "https://files.pythonhosted.org/packages/f6/86/6d0a5cc56481ac61977b7c839677ed5c63d38cf0fcb3e2280843a8a6f476/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451", size = 592321 }, + { url = "https://files.pythonhosted.org/packages/5d/87/d1e2453fe336f71e6aa296452a8c85c2118b587b1d25ce98014f75838a60/rpds_py-0.25.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f", size = 558162 }, + { url = "https://files.pythonhosted.org/packages/ad/92/349f04b1644c5cef3e2e6c53b7168a28531945f9e6fca7425f6d20ddbc3c/rpds_py-0.25.1-cp39-cp39-win32.whl", hash = "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449", size = 219920 }, + { url = "https://files.pythonhosted.org/packages/f2/84/3969bef883a3f37ff2213795257cb7b7e93a115829670befb8de0e003031/rpds_py-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890", size = 231452 }, + { url = "https://files.pythonhosted.org/packages/78/ff/566ce53529b12b4f10c0a348d316bd766970b7060b4fd50f888be3b3b281/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", size = 373931 }, + { url = "https://files.pythonhosted.org/packages/83/5d/deba18503f7c7878e26aa696e97f051175788e19d5336b3b0e76d3ef9256/rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", size = 359074 }, + { url = "https://files.pythonhosted.org/packages/0d/74/313415c5627644eb114df49c56a27edba4d40cfd7c92bd90212b3604ca84/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", size = 387255 }, + { url = "https://files.pythonhosted.org/packages/8c/c8/c723298ed6338963d94e05c0f12793acc9b91d04ed7c4ba7508e534b7385/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", size = 400714 }, + { url = "https://files.pythonhosted.org/packages/33/8a/51f1f6aa653c2e110ed482ef2ae94140d56c910378752a1b483af11019ee/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", size = 523105 }, + { url = "https://files.pythonhosted.org/packages/c7/a4/7873d15c088ad3bff36910b29ceb0f178e4b3232c2adbe9198de68a41e63/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", size = 411499 }, + { url = "https://files.pythonhosted.org/packages/90/f3/0ce1437befe1410766d11d08239333ac1b2d940f8a64234ce48a7714669c/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", size = 387918 }, + { url = "https://files.pythonhosted.org/packages/94/d4/5551247988b2a3566afb8a9dba3f1d4a3eea47793fd83000276c1a6c726e/rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", size = 421705 }, + { url = "https://files.pythonhosted.org/packages/b0/25/5960f28f847bf736cc7ee3c545a7e1d2f3b5edaf82c96fb616c2f5ed52d0/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", size = 564489 }, + { url = "https://files.pythonhosted.org/packages/02/66/1c99884a0d44e8c2904d3c4ec302f995292d5dde892c3bf7685ac1930146/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", size = 592557 }, + { url = "https://files.pythonhosted.org/packages/55/ae/4aeac84ebeffeac14abb05b3bb1d2f728d00adb55d3fb7b51c9fa772e760/rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", size = 558691 }, + { url = "https://files.pythonhosted.org/packages/41/b3/728a08ff6f5e06fe3bb9af2e770e9d5fd20141af45cff8dfc62da4b2d0b3/rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", size = 231651 }, + { url = "https://files.pythonhosted.org/packages/49/74/48f3df0715a585cbf5d34919c9c757a4c92c1a9eba059f2d334e72471f70/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", size = 374208 }, + { url = "https://files.pythonhosted.org/packages/55/b0/9b01bb11ce01ec03d05e627249cc2c06039d6aa24ea5a22a39c312167c10/rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", size = 359262 }, + { url = "https://files.pythonhosted.org/packages/a9/eb/5395621618f723ebd5116c53282052943a726dba111b49cd2071f785b665/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", size = 387366 }, + { url = "https://files.pythonhosted.org/packages/68/73/3d51442bdb246db619d75039a50ea1cf8b5b4ee250c3e5cd5c3af5981cd4/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", size = 400759 }, + { url = "https://files.pythonhosted.org/packages/b7/4c/3a32d5955d7e6cb117314597bc0f2224efc798428318b13073efe306512a/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", size = 523128 }, + { url = "https://files.pythonhosted.org/packages/be/95/1ffccd3b0bb901ae60b1dd4b1be2ab98bb4eb834cd9b15199888f5702f7b/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", size = 411597 }, + { url = "https://files.pythonhosted.org/packages/ef/6d/6e6cd310180689db8b0d2de7f7d1eabf3fb013f239e156ae0d5a1a85c27f/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", size = 388053 }, + { url = "https://files.pythonhosted.org/packages/4a/87/ec4186b1fe6365ced6fa470960e68fc7804bafbe7c0cf5a36237aa240efa/rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", size = 421821 }, + { url = "https://files.pythonhosted.org/packages/7a/60/84f821f6bf4e0e710acc5039d91f8f594fae0d93fc368704920d8971680d/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", size = 564534 }, + { url = "https://files.pythonhosted.org/packages/41/3a/bc654eb15d3b38f9330fe0f545016ba154d89cdabc6177b0295910cd0ebe/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", size = 592674 }, + { url = "https://files.pythonhosted.org/packages/2e/ba/31239736f29e4dfc7a58a45955c5db852864c306131fd6320aea214d5437/rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", size = 558781 }, + { url = "https://files.pythonhosted.org/packages/78/b2/198266f070c6760e0e8cd00f9f2b9c86133ceebbe7c6d114bdcfea200180/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b", size = 373973 }, + { url = "https://files.pythonhosted.org/packages/13/79/1265eae618f88aa5d5e7122bd32dd41700bafe5a8bcea404e998848cd844/rpds_py-0.25.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23", size = 359326 }, + { url = "https://files.pythonhosted.org/packages/30/ab/6913b96f3ac072e87e76e45fe938263b0ab0d78b6b2cef3f2e56067befc0/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e", size = 387544 }, + { url = "https://files.pythonhosted.org/packages/b0/23/129ed12d25229acc6deb8cbe90baadd8762e563c267c9594eb2fcc15be0c/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7", size = 400240 }, + { url = "https://files.pythonhosted.org/packages/b5/e0/6811a38a5efa46b7ee6ed2103c95cb9abb16991544c3b69007aa679b6944/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83", size = 525599 }, + { url = "https://files.pythonhosted.org/packages/6c/10/2dc88bcaa0d86bdb59e017a330b1972ffeeb7f5061bb5a180c9a2bb73bbf/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b", size = 411154 }, + { url = "https://files.pythonhosted.org/packages/cf/d1/a72d522eb7d934fb33e9c501e6ecae00e2035af924d4ff37d964e9a3959b/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf", size = 388297 }, + { url = "https://files.pythonhosted.org/packages/55/90/0dd7169ec74f042405b6b73512200d637a3088c156f64e1c07c18aa2fe59/rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1", size = 421894 }, + { url = "https://files.pythonhosted.org/packages/37/e9/45170894add451783ed839c5c4a495e050aa8baa06d720364d9dff394dac/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1", size = 564409 }, + { url = "https://files.pythonhosted.org/packages/59/d0/31cece9090e76fbdb50c758c165d40da604b03b37c3ba53f010bbfeb130a/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf", size = 592681 }, + { url = "https://files.pythonhosted.org/packages/f1/4c/22ef535efb2beec614ba7be83e62b439eb83b0b0d7b1775e22d35af3f9b5/rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992", size = 558744 }, + { url = "https://files.pythonhosted.org/packages/79/ff/f2150efc8daf0581d4dfaf0a2a30b08088b6df900230ee5ae4f7c8cd5163/rpds_py-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793", size = 231305 }, +] + +[[package]] +name = "ruff" +version = "0.11.12" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/0a/92416b159ec00cdf11e5882a9d80d29bf84bba3dbebc51c4898bfbca1da6/ruff-0.11.12.tar.gz", hash = "sha256:43cf7f69c7d7c7d7513b9d59c5d8cafd704e05944f978614aa9faff6ac202603", size = 4202289 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/cc/53eb79f012d15e136d40a8e8fc519ba8f55a057f60b29c2df34efd47c6e3/ruff-0.11.12-py3-none-linux_armv6l.whl", hash = "sha256:c7680aa2f0d4c4f43353d1e72123955c7a2159b8646cd43402de6d4a3a25d7cc", size = 10285597 }, + { url = "https://files.pythonhosted.org/packages/e7/d7/73386e9fb0232b015a23f62fea7503f96e29c29e6c45461d4a73bac74df9/ruff-0.11.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2cad64843da9f134565c20bcc430642de897b8ea02e2e79e6e02a76b8dcad7c3", size = 11053154 }, + { url = "https://files.pythonhosted.org/packages/4e/eb/3eae144c5114e92deb65a0cb2c72326c8469e14991e9bc3ec0349da1331c/ruff-0.11.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:9b6886b524a1c659cee1758140138455d3c029783d1b9e643f3624a5ee0cb0aa", size = 10403048 }, + { url = "https://files.pythonhosted.org/packages/29/64/20c54b20e58b1058db6689e94731f2a22e9f7abab74e1a758dfba058b6ca/ruff-0.11.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cc3a3690aad6e86c1958d3ec3c38c4594b6ecec75c1f531e84160bd827b2012", size = 10597062 }, + { url = "https://files.pythonhosted.org/packages/29/3a/79fa6a9a39422a400564ca7233a689a151f1039110f0bbbabcb38106883a/ruff-0.11.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f97fdbc2549f456c65b3b0048560d44ddd540db1f27c778a938371424b49fe4a", size = 10155152 }, + { url = "https://files.pythonhosted.org/packages/e5/a4/22c2c97b2340aa968af3a39bc38045e78d36abd4ed3fa2bde91c31e712e3/ruff-0.11.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74adf84960236961090e2d1348c1a67d940fd12e811a33fb3d107df61eef8fc7", size = 11723067 }, + { url = "https://files.pythonhosted.org/packages/bc/cf/3e452fbd9597bcd8058856ecd42b22751749d07935793a1856d988154151/ruff-0.11.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b56697e5b8bcf1d61293ccfe63873aba08fdbcbbba839fc046ec5926bdb25a3a", size = 12460807 }, + { url = "https://files.pythonhosted.org/packages/2f/ec/8f170381a15e1eb7d93cb4feef8d17334d5a1eb33fee273aee5d1f8241a3/ruff-0.11.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d47afa45e7b0eaf5e5969c6b39cbd108be83910b5c74626247e366fd7a36a13", size = 12063261 }, + { url = "https://files.pythonhosted.org/packages/0d/bf/57208f8c0a8153a14652a85f4116c0002148e83770d7a41f2e90b52d2b4e/ruff-0.11.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:692bf9603fe1bf949de8b09a2da896f05c01ed7a187f4a386cdba6760e7f61be", size = 11329601 }, + { url = "https://files.pythonhosted.org/packages/c3/56/edf942f7fdac5888094d9ffa303f12096f1a93eb46570bcf5f14c0c70880/ruff-0.11.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08033320e979df3b20dba567c62f69c45e01df708b0f9c83912d7abd3e0801cd", size = 11522186 }, + { url = "https://files.pythonhosted.org/packages/ed/63/79ffef65246911ed7e2290aeece48739d9603b3a35f9529fec0fc6c26400/ruff-0.11.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:929b7706584f5bfd61d67d5070f399057d07c70585fa8c4491d78ada452d3bef", size = 10449032 }, + { url = "https://files.pythonhosted.org/packages/88/19/8c9d4d8a1c2a3f5a1ea45a64b42593d50e28b8e038f1aafd65d6b43647f3/ruff-0.11.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7de4a73205dc5756b8e09ee3ed67c38312dce1aa28972b93150f5751199981b5", size = 10129370 }, + { url = "https://files.pythonhosted.org/packages/bc/0f/2d15533eaa18f460530a857e1778900cd867ded67f16c85723569d54e410/ruff-0.11.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2635c2a90ac1b8ca9e93b70af59dfd1dd2026a40e2d6eebaa3efb0465dd9cf02", size = 11123529 }, + { url = "https://files.pythonhosted.org/packages/4f/e2/4c2ac669534bdded835356813f48ea33cfb3a947dc47f270038364587088/ruff-0.11.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d05d6a78a89166f03f03a198ecc9d18779076ad0eec476819467acb401028c0c", size = 11577642 }, + { url = "https://files.pythonhosted.org/packages/a7/9b/c9ddf7f924d5617a1c94a93ba595f4b24cb5bc50e98b94433ab3f7ad27e5/ruff-0.11.12-py3-none-win32.whl", hash = "sha256:f5a07f49767c4be4772d161bfc049c1f242db0cfe1bd976e0f0886732a4765d6", size = 10475511 }, + { url = "https://files.pythonhosted.org/packages/fd/d6/74fb6d3470c1aada019ffff33c0f9210af746cca0a4de19a1f10ce54968a/ruff-0.11.12-py3-none-win_amd64.whl", hash = "sha256:5a4d9f8030d8c3a45df201d7fb3ed38d0219bccd7955268e863ee4a115fa0832", size = 11523573 }, + { url = "https://files.pythonhosted.org/packages/44/42/d58086ec20f52d2b0140752ae54b355ea2be2ed46f914231136dd1effcc7/ruff-0.11.12-py3-none-win_arm64.whl", hash = "sha256:65194e37853158d368e333ba282217941029a28ea90913c67e558c611d04daa5", size = 10697770 }, +] + +[[package]] +name = "runs" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "xmod" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/6d/b9aace390f62db5d7d2c77eafce3d42774f27f1829d24fa9b6f598b3ef71/runs-1.2.2.tar.gz", hash = "sha256:9dc1815e2895cfb3a48317b173b9f1eac9ba5549b36a847b5cc60c3bf82ecef1", size = 5474 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/d6/17caf2e4af1dec288477a0cbbe4a96fbc9b8a28457dce3f1f452630ce216/runs-1.2.2-py3-none-any.whl", hash = "sha256:0980dcbc25aba1505f307ac4f0e9e92cbd0be2a15a1e983ee86c24c87b839dfd", size = 7033 }, +] + +[[package]] +name = "shellingham" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "swagger-parser" +version = "1.0.1" +source = { git = "https://github.com/jakedialpad/swagger-parser?rev=v1.0.1b#13f21d59aef87b2babb28c9423183c341333a009" } +dependencies = [ + { name = "jinja2" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "swagger-spec-validator" }, +] + +[[package]] +name = "swagger-spec-validator" +version = "3.0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-resources" }, + { name = "jsonschema" }, + { name = "pyyaml" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/e9/d0a4a1e4ed6b4b805d5465affaeaa2d91ae08a8aae966f4bb7402e23ee37/swagger_spec_validator-3.0.4.tar.gz", hash = "sha256:637ac6d865270bfcd07df24605548e6e1f1d9c39adcfd855da37fa3fdebfed4b", size = 22355 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/ac/31ba87a959b19e640ebc18851438b82b5b66cef02ad31da7468d1d8bd625/swagger_spec_validator-3.0.4-py2.py3-none-any.whl", hash = "sha256:1a2a4f4f7076479ae7835d892dd53952ccca9414efa172c440c775cf0ac01f48", size = 28473 }, +] + +[[package]] +name = "swagger-stub" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpretty" }, + { name = "pytest" }, + { name = "swagger-parser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f0/dc/458bad9fbf7aa5747581ae0bde76fc087a61e2aed7852d269c6872b5a50c/swagger_stub-0.2.1.tar.gz", hash = "sha256:6ff47e489e183a5f981de9554228750a738cd18aaf2c7b140bff3680a0317db1", size = 18658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/4c/b41ed3f24142f3c8655286aeef928666c50708af4c075ec5173e8eca8b2e/swagger_stub-0.2.1-py2.py3-none-any.whl", hash = "sha256:84eb254ccf94f6adb67a3a658e7f3f5a2d5007b3e32784b9b6461cde54c36c23", size = 7898 }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077 }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429 }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067 }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030 }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898 }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894 }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319 }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273 }, + { url = "https://files.pythonhosted.org/packages/72/6f/fa64ef058ac1446a1e51110c375339b3ec6be245af9d14c87c4a6412dd32/tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff", size = 98310 }, + { url = "https://files.pythonhosted.org/packages/6a/1c/4a2dcde4a51b81be3530565e92eda625d94dafb46dbeb15069df4caffc34/tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b", size = 108309 }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453 }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486 }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349 }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159 }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243 }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645 }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584 }, + { url = "https://files.pythonhosted.org/packages/1c/9a/47c0449b98e6e7d1be6cbac02f93dd79003234ddc4aaab6ba07a9a7482e2/tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98", size = 98875 }, + { url = "https://files.pythonhosted.org/packages/ef/60/9b9638f081c6f1261e2688bd487625cd1e660d0a85bd469e91d8db969734/tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4", size = 109418 }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708 }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582 }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543 }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691 }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170 }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530 }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666 }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954 }, + { url = "https://files.pythonhosted.org/packages/5b/b9/1ed31d167be802da0fc95020d04cd27b7d7065cc6fbefdd2f9186f60d7bd/tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec", size = 98724 }, + { url = "https://files.pythonhosted.org/packages/c7/32/b0963458706accd9afcfeb867c0f9175a741bf7b19cd424230714d722198/tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69", size = 109383 }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257 }, +] + +[[package]] +name = "typer" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "rich" }, + { name = "shellingham" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317 }, +] + +[[package]] +name = "typing-extensions" +version = "4.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d1/bc/51647cd02527e87d05cb083ccc402f93e441606ff1f01739a62c8ad09ba5/typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", size = 107423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/e0/552843e0d356fbb5256d21449fa957fa4eff3bbc135a74a691ee70c7c5da/typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af", size = 43839 }, +] + +[[package]] +name = "tzdata" +version = "2025.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839 }, +] + +[[package]] +name = "urllib3" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "werkzeug" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/32/af/d4502dc713b4ccea7175d764718d5183caf8d0867a4f0190d5d4a45cea49/werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4", size = 806453 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371 }, +] + +[[package]] +name = "xmod" +version = "1.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/b2/e3edc608823348e628a919e1d7129e641997afadd946febdd704aecc5881/xmod-1.8.1.tar.gz", hash = "sha256:38c76486b9d672c546d57d8035df0beb7f4a9b088bc3fb2de5431ae821444377", size = 3988 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/0dc75b64a764ea1cb8e4c32d1fb273c147304d4e5483cd58be482dc62e45/xmod-1.8.1-py3-none-any.whl", hash = "sha256:a24e9458a4853489042522bdca9e50ee2eac5ab75c809a91150a8a7f40670d48", size = 4610 }, +] + +[[package]] +name = "zipp" +version = "3.22.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/12/b6/7b3d16792fdf94f146bed92be90b4eb4563569eca91513c8609aebf0c167/zipp-3.22.0.tar.gz", hash = "sha256:dd2f28c3ce4bc67507bfd3781d21b7bb2be31103b51a4553ad7d90b84e57ace5", size = 25257 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/da/f64669af4cae46f17b90798a827519ce3737d31dbafad65d391e49643dc4/zipp-3.22.0-py3-none-any.whl", hash = "sha256:fe208f65f2aca48b81f9e6fd8cf7b8b32c26375266b009b413d45306b6148343", size = 9796 }, +]