# Create an activity code Source: https://docs.smokeball.com/api-reference/activity-codes/create-an-activity-code /openapi.json post /activitycodes Creates an activity code. # Delete an activity code Source: https://docs.smokeball.com/api-reference/activity-codes/delete-an-activity-code /openapi.json delete /activitycodes/{id} Deletes an activity code. # Get activity codes Source: https://docs.smokeball.com/api-reference/activity-codes/get-activity-codes /openapi.json get /activitycodes Returns a list of activity codes. # Get an activity code Source: https://docs.smokeball.com/api-reference/activity-codes/get-an-activity-code /openapi.json get /activitycodes/{id} Retrieves a specified activity code. # Update an activity code Source: https://docs.smokeball.com/api-reference/activity-codes/update-an-activity-code /openapi.json put /activitycodes/{id} Updates an activity code. # Create an authorization group Source: https://docs.smokeball.com/api-reference/authorization/create-an-authorization-group /openapi.json post /groups Creates an authorization group. # Create an authorization policy Source: https://docs.smokeball.com/api-reference/authorization/create-an-authorization-policy /openapi.json post /policies Creates an authorization policy. # Deletes an authorization group Source: https://docs.smokeball.com/api-reference/authorization/deletes-an-authorization-group /openapi.json delete /groups/{id} Delete an authorization group. # Get an authorization group Source: https://docs.smokeball.com/api-reference/authorization/get-an-authorization-group /openapi.json get /groups/{id} Retrieves a specified authorization group. # Get an authorization policy Source: https://docs.smokeball.com/api-reference/authorization/get-an-authorization-policy /openapi.json get /policies/{reference} Retrieves a specified authorization policy. # Get authorization groups Source: https://docs.smokeball.com/api-reference/authorization/get-authorization-groups /openapi.json get /groups Retrieves auhorization groups for the firm associated with the authenticated client. # Update an authorization group Source: https://docs.smokeball.com/api-reference/authorization/update-an-authorization-group /openapi.json put /groups/{id} Updates a specified authorization group. # Update an authorization policy Source: https://docs.smokeball.com/api-reference/authorization/update-an-authorization-policy /openapi.json put /policies/{reference} Updates a specified authorization policy. # Create a requisition Source: https://docs.smokeball.com/api-reference/bank-accounts/create-a-requisition /openapi.json post /bankaccounts/{id}/requisitions Creates a requisition against a trust account and supports the account approval workflow. Whether the requisition requires approval depends on the trust account configuration: - If approvals are enabled, firm owners can create requisitions without approval, while all other users require approval. - If approvals are disabled, the requisition is automatically approved upon creation. # Create a transaction Source: https://docs.smokeball.com/api-reference/bank-accounts/create-a-transaction /openapi.json post /bankaccounts/{bankAccountId}/transactions Creates a transaction. # Get a bank account Source: https://docs.smokeball.com/api-reference/bank-accounts/get-a-bank-account /openapi.json get /bankaccounts/{id} Retrieves a specified bank account. # Get a protected bank account balance Source: https://docs.smokeball.com/api-reference/bank-accounts/get-a-protected-bank-account-balance /openapi.json get /bankaccounts/{id}/protected-balances/{protectionId} Retrieves a specified protected account balance record. # Get a transaction Source: https://docs.smokeball.com/api-reference/bank-accounts/get-a-transaction /openapi.json get /bankaccounts/{bankAccountId}/transactions/{id} Retrieves a specified bank account transaction. # Get bank account matter balances Source: https://docs.smokeball.com/api-reference/bank-accounts/get-bank-account-matter-balances /openapi.json get /bankaccounts/{id}/matter-balances Returns a list of bank account matter balances. # Get bank accounts Source: https://docs.smokeball.com/api-reference/bank-accounts/get-bank-accounts /openapi.json get /bankaccounts Returns a list of bank accounts. # Get transactions Source: https://docs.smokeball.com/api-reference/bank-accounts/get-transactions /openapi.json get /bankaccounts/{bankAccountId}/transactions Retrieves a specified bank accounts transaction. # Protect bank account funds Source: https://docs.smokeball.com/api-reference/bank-accounts/protect-bank-account-funds /openapi.json post /bankaccounts/{id}/protect-funds Protects funds in a trust bank account. Only trust accounts are supported. # Unprotect bank account funds Source: https://docs.smokeball.com/api-reference/bank-accounts/unprotect-bank-account-funds /openapi.json post /bankaccounts/{id}/unprotect-funds/{protectionId} Unprotects funds in a trust bank account. Only trust accounts are supported. # Add tags to a contact Source: https://docs.smokeball.com/api-reference/contacts/add-tags-to-a-contact /openapi.json post /contacts/{id}/tags Adds tags to a contact. # Create a contact Source: https://docs.smokeball.com/api-reference/contacts/create-a-contact /openapi.json post /contacts Creates a contact. # Create a contact relation Source: https://docs.smokeball.com/api-reference/contacts/create-a-contact-relation /openapi.json post /contacts/{contactId}/relations Creates a relation for a specified contact. # Delete a contact Source: https://docs.smokeball.com/api-reference/contacts/delete-a-contact /openapi.json delete /contacts/{id} Deletes a specified contact. # Delete a contact relation Source: https://docs.smokeball.com/api-reference/contacts/delete-a-contact-relation /openapi.json delete /contacts/{contactId}/relations/{relatedContactId} Deletes a relation for a specified contact. # Get a contact Source: https://docs.smokeball.com/api-reference/contacts/get-a-contact /openapi.json get /contacts/{id} Retrieves a specified contact. # Get a contact relation Source: https://docs.smokeball.com/api-reference/contacts/get-a-contact-relation /openapi.json get /contacts/{contactId}/relations/{relatedContactId} Retrieves a relation for a specified contact if associated with the authenticated client. # Get contact relations Source: https://docs.smokeball.com/api-reference/contacts/get-contact-relations /openapi.json get /contacts/{contactId}/relations Returns a list of relations for a specified contact if associated with the authenticated client. # Get contact tags Source: https://docs.smokeball.com/api-reference/contacts/get-contact-tags /openapi.json get /contacts/{id}/tags Retrieves a specified contacts tags. # Get contacts Source: https://docs.smokeball.com/api-reference/contacts/get-contacts /openapi.json get /contacts Returns a list of contacts. # Get contacts by ids Source: https://docs.smokeball.com/api-reference/contacts/get-contacts-by-ids /openapi.json post /contacts/batch Returns the contacts matching the supplied IDs, in the same order as the request. IDs that do not resolve, or that belong to another account, are omitted. # Remove tags from a contact Source: https://docs.smokeball.com/api-reference/contacts/remove-tags-from-a-contact /openapi.json delete /contacts/{id}/tags/{tagId} Removes tags from a contact. # Update a contact Source: https://docs.smokeball.com/api-reference/contacts/update-a-contact /openapi.json put /contacts/{id} Updates a specified contact. # Update a contact relation Source: https://docs.smokeball.com/api-reference/contacts/update-a-contact-relation /openapi.json put /contacts/{contactId}/relations/{relatedContactId} Updates a relation for a specified contact. # Create event Source: https://docs.smokeball.com/api-reference/events/create-event /openapi.json post /events Create new event. # Create reminder for an event Source: https://docs.smokeball.com/api-reference/events/create-reminder-for-an-event /openapi.json post /events/{eventId}/reminders Creates a new reminder for an event. # Delete event Source: https://docs.smokeball.com/api-reference/events/delete-event /openapi.json delete /events/{eventId} Deletes a specified event. # Delete reminder for an event Source: https://docs.smokeball.com/api-reference/events/delete-reminder-for-an-event /openapi.json delete /events/{eventId}/reminders/{reminderId} Deletes a specified event reminder. # Get an event Source: https://docs.smokeball.com/api-reference/events/get-an-event /openapi.json get /events/{eventId} Retrieves a specified event. # Get events Source: https://docs.smokeball.com/api-reference/events/get-events /openapi.json get /events Returns a list of events. # Gets reminders for an event Source: https://docs.smokeball.com/api-reference/events/gets-reminders-for-an-event /openapi.json get /events/{eventId}/reminders Returns the collection of reminders for an event # Update a reminder for an event Source: https://docs.smokeball.com/api-reference/events/update-a-reminder-for-an-event /openapi.json put /events/{eventId}/reminders/{reminderId} Updates an existing reminder for an event. # Update event Source: https://docs.smokeball.com/api-reference/events/update-event /openapi.json put /events/{eventId} Updates event details. # Create an expense Source: https://docs.smokeball.com/api-reference/expenses/create-an-expense /openapi.json post /matters/{matterId}/expenses Creates an expense for the specified matter. # Delete an expense Source: https://docs.smokeball.com/api-reference/expenses/delete-an-expense /openapi.json delete /matters/{matterId}/expenses/{id} Deletes an expense. # Get an expense Source: https://docs.smokeball.com/api-reference/expenses/get-an-expense /openapi.json get /matters/{matterId}/expenses/{id} Retrieves a specified expense. # Get expenses Source: https://docs.smokeball.com/api-reference/expenses/get-expenses /openapi.json get /matters/{matterId}/expenses Returns a list of expenses for the specified matter. # Patch an expense Source: https://docs.smokeball.com/api-reference/expenses/patch-an-expense /openapi.json patch /matters/{matterId}/expenses/{id} Patches an expense for the specified matter. # Update an expense Source: https://docs.smokeball.com/api-reference/expenses/update-an-expense /openapi.json put /matters/{matterId}/expenses/{id} Updates an expense for the specified matter. # Create a fee Source: https://docs.smokeball.com/api-reference/fees/create-a-fee /openapi.json post /matters/{matterId}/fees Creates a fee for the specified matter. # Delete a fee Source: https://docs.smokeball.com/api-reference/fees/delete-a-fee /openapi.json delete /matters/{matterId}/fees/{id} Deletes a fee. # Get a fee Source: https://docs.smokeball.com/api-reference/fees/get-a-fee /openapi.json get /matters/{matterId}/fees/{id} Retrieves a specified fee. # Get fees Source: https://docs.smokeball.com/api-reference/fees/get-fees /openapi.json get /matters/{matterId}/fees Returns a list of fees for the specified matter. # Patch a fee Source: https://docs.smokeball.com/api-reference/fees/patch-a-fee /openapi.json patch /matters/{matterId}/fees/{id} Patches a fee for the specified matter. # Update a fee Source: https://docs.smokeball.com/api-reference/fees/update-a-fee /openapi.json put /matters/{matterId}/fees/{id} Updates a fee for the specified matter. # Add file to a matter Source: https://docs.smokeball.com/api-reference/files/add-file-to-a-matter /openapi.json post /matters/{matterId}/documents/files Registers a new file on the matter and returns a presigned PUT URL (valid for 1 hour) to upload its content. The file is created only after the content has been PUT to the URL and confirmed by the storage callback; the 'Location' link to 'GetFileById' returns 404 until then. 'fileName' is split on the last '.' into name and extension. The name is normalised to XML-safe characters and must contain at least one valid character. 'folderId' and 'folderPath' are mutually exclusive (400 when both are set). 'folderId' is not validated for existence or matter membership; an unknown value is accepted as-is. Additional data values must be strings, integers, numbers, booleans or null; keys must not be blank. # Add files to a matter Source: https://docs.smokeball.com/api-reference/files/add-files-to-a-matter /openapi.json post /matters/{matterId}/documents/files/bulk Registers a batch of new files on the matter and returns one 'UploadFileInfo' (presigned PUT URL, valid for 1 hour) per file, in request order. Intended for data migrations. The same rules as 'AddFile' apply to each item. The operation is not atomic: items are validated in order and a 400 on item n leaves the upload requests for items before it in place. No 'Location' header is returned. There is no cap on the array size. # Delete a file Source: https://docs.smokeball.com/api-reference/files/delete-a-file /openapi.json delete /matters/{matterId}/documents/files/{fileId} Deletes a specified matter file. # Get a file Source: https://docs.smokeball.com/api-reference/files/get-a-file /openapi.json get /matters/{matterId}/documents/files/{fileId} Retrieves a specified file. The matterId in the route is used for authorisation only and is not cross-checked against the matter the file belongs to. # Get download url for a file Source: https://docs.smokeball.com/api-reference/files/get-download-url-for-a-file /openapi.json get /matters/{matterId}/documents/files/{fileId}/download Returns a presigned download URL for the current version of the file, or for a specific version when 'versionId' is supplied. The URL is valid for 1 hour. The response 'Content-Disposition' uses the file name and extension. # Get file history on a matter Source: https://docs.smokeball.com/api-reference/files/get-file-history-on-a-matter /openapi.json get /matters/{matterId}/documents/files/{fileId}/history Returns the full list of history entries for the specified file. The result is not paged. # Get files on a matter Source: https://docs.smokeball.com/api-reference/files/get-files-on-a-matter /openapi.json get /matters/{matterId}/documents/files Returns a paged list of files associated with a matter. Deleted files are excluded. Paging defaults: offset 0, limit equal to the maximum page size when not supplied. Ordering: ascending by date created (oldest first) unless a sort option is supplied. Three query modes are used depending on the supplied options: - Search (a search query is supplied): results come from the document search index. Sort, paging and the cursor all apply. - Cursor only (no search query): returns files modified since the cursor. Sort options are ignored in this mode. - Standard (neither): sort, paging and file extension include/exclude filters apply. # Get or create a preview Source: https://docs.smokeball.com/api-reference/files/get-or-create-a-preview /openapi.json post /matters/{matterId}/documents/files/{fileId}/preview Returns a PDF preview of the current version of the specified file, creating it if it does not exist yet. The 202 response has two shapes: - Preview already exists: 'downloadUrl' contains a presigned URL to the PDF preview and 'expiry' is its expiry time. The URL is valid for 1 day. - Preview does not exist yet: a conversion is requested and processed asynchronously; 'downloadUrl' and 'expiry' are null. Poll the 'Location' header (GetPreviewById) until it returns 200. The preview is always generated as a PDF, regardless of the source file type. Previews are keyed by document version, so uploading a new version of the file produces a new preview. # Get preview info for a file Source: https://docs.smokeball.com/api-reference/files/get-preview-info-for-a-file /openapi.json get /matters/{matterId}/documents/files/{fileId}/preview Returns a presigned URL to the PDF preview of the current version of the specified file. The URL is valid for 1 day from the time of the request. This endpoint never creates a preview. Use CreatePreview to request one; while the conversion is still pending this endpoint returns 404. # Get preview info for a file by document version Source: https://docs.smokeball.com/api-reference/files/get-preview-info-for-a-file-by-document-version /openapi.json get /matters/{matterId}/documents/files/{fileId}/preview/{documentVersionId} Returns a presigned URL to the PDF preview of a specific document version of the file. The URL is valid for 1 day from the time of the request. # Get upload url for a file Source: https://docs.smokeball.com/api-reference/files/get-upload-url-for-a-file /openapi.json get /matters/{matterId}/documents/files/{fileId}/upload Returns a presigned PUT URL for uploading a new version of an existing file. The URL is valid for 1 hour. # Patch a file Source: https://docs.smokeball.com/api-reference/files/patch-a-file /openapi.json patch /matters/{matterId}/documents/files/{fileId} Partially updates a file's metadata. Only the fields supplied in the request are changed; all other fields keep their current values. Use PUT to replace all fields. The response always includes a presigned PUT URL (valid for 1 hour) that can optionally be used to upload a new version of the file. Every call records a new upload request whether or not the URL is used. # Upload/update version or metadata of a file Source: https://docs.smokeball.com/api-reference/files/uploadupdate-version-or-metadata-of-a-file /openapi.json put /matters/{matterId}/documents/files/{fileId} Replaces all editable fields of a file with the supplied values. Use PATCH to change only some fields. Because this is a full replace, omitting 'folderId' (or setting it to null) moves the file to the root folder. The destination folder is not validated for existence. 'fileName' is required (maximum 256 characters). 'userId' falls back to the authenticated user when omitted. Additional data values are merged with existing keys; null or empty additional data is a no-op. 'isFavorite' is ignored when null. The response always includes a presigned PUT URL (valid for 1 hour) that can optionally be used to upload a new version of the file. Every call records a new upload request whether or not the URL is used. # Create firm Source: https://docs.smokeball.com/api-reference/firm/create-firm /openapi.json post /firm Creates a firm for the current authenticated client and registers an existing user as a staff member and firm owner. # Delete firm user mapping Source: https://docs.smokeball.com/api-reference/firm/delete-firm-user-mapping /openapi.json delete /firmusers/{id} Removes the user from the firm associated with the authenticated client. The staff member is set as a former staff member and a former user. User access is also disabled. # Get firm Source: https://docs.smokeball.com/api-reference/firm/get-firm /openapi.json get /firm Retrieves the details of the firm associated with the authenticated client. # Get firm user mapping Source: https://docs.smokeball.com/api-reference/firm/get-firm-user-mapping /openapi.json get /firmusers/{id} Retrieves a user mapping (based on staff or user id parameter provided) in the firm associated with the authenticated client. # Get firm user mappings Source: https://docs.smokeball.com/api-reference/firm/get-firm-user-mappings /openapi.json get /firmusers Retrieves a list of user mappings in the firm associated with the authenticated client. # Update firm Source: https://docs.smokeball.com/api-reference/firm/update-firm /openapi.json put /firm Updates firm details for the current authenticated client. # Update firm user mapping Source: https://docs.smokeball.com/api-reference/firm/update-firm-user-mapping /openapi.json put /firmusers/{id} Updates a user mapping in the firm associated with the authenticated client. # Create a folder Source: https://docs.smokeball.com/api-reference/folders/create-a-folder /openapi.json post /matters/{matterId}/documents/folders Creates a new folder under the specified matter. When `parentFolderId` is null or omitted the folder is created under the matter root. # Delete a folder Source: https://docs.smokeball.com/api-reference/folders/delete-a-folder /openapi.json delete /matters/{matterId}/documents/folders/{folderId} Deletes a folder belonging to the specified matter. An unknown folder id returns 400 rather than 404, because the folder may have been created but not yet processed. The change is processed asynchronously; the returned Link continues to resolve the folder with `isDeleted: true` once applied. # Get folder history on a matter Source: https://docs.smokeball.com/api-reference/folders/get-folder-history-on-a-matter /openapi.json get /matters/{matterId}/documents/folders/{folderId}/history Returns a list of history entries for the specified folder. The response is not paged. An unknown folder id returns 200 with an empty collection rather than 404. # Get folder path hierarchy Source: https://docs.smokeball.com/api-reference/folders/get-folder-path-hierarchy /openapi.json get /matters/{matterId}/documents/folders/{folderId}/path Returns the full folder hierarchy (path) as a collection ordered from the root folder down to the supplied folder. The root folder is not included in the response. # Get folders/files under a folder Source: https://docs.smokeball.com/api-reference/folders/get-foldersfiles-under-a-folder /openapi.json get /matters/{matterId}/documents/folders/{folderId} Returns a list of sub folders and files under a specified folder of a matter. The `parentId` and `name` properties of the response describe the requested folder itself, not its children. A deleted folder is still returned, with `isDeleted: true` and an empty listing. Folders are listed before files. Paging applies across the combined list: `offset` defaults to 0 and `limit` defaults to the maximum page size (`PagingOptions.MaxPageSize`). Deleted sub folders and deleted files are excluded. # Get folders/files under root folder Source: https://docs.smokeball.com/api-reference/folders/get-foldersfiles-under-root-folder /openapi.json get /matters/{matterId}/documents/folders Returns a list of sub folders and files under the root folder of a matter. Folders are listed before files. Paging applies across the combined list: `offset` defaults to 0 and `limit` defaults to the maximum page size (`PagingOptions.MaxPageSize`). Deleted folders and deleted files are excluded. # Patch a folder Source: https://docs.smokeball.com/api-reference/folders/patch-a-folder /openapi.json patch /matters/{matterId}/documents/folders/{folderId} Partially updates a folder belonging to the specified matter. Only the fields supplied in the request are applied. A deleted folder can be restored by setting `isDeleted` to `false`. Only the folder itself is restored; subfolders and files that were deleted remain deleted. Other fields supplied in the same request are applied after the restore. # Update a folder Source: https://docs.smokeball.com/api-reference/folders/update-a-folder /openapi.json put /matters/{matterId}/documents/folders/{folderId} Replaces the editable fields of a folder belonging to the specified matter. All fields are applied, not only the ones that changed: `name` is required, and omitting `parentFolderId` (or setting it to null) moves the folder to the matter root. Use PATCH to change individual fields. The destination folder must be on the same matter (400 otherwise); a destination that does not exist is not rejected. If `userId` is omitted the authenticated user is recorded. A deleted folder cannot be updated with PUT; use PATCH with `isDeleted: false` to restore it. # Get mapping Source: https://docs.smokeball.com/api-reference/integrated-search/get-mapping /openapi.json get /matters/{matterId}/integrated-search/mapping Retrieves integrated search mapping data associated with the authenticated client. Uses the integrated search provider used by the firm (e.g. InfoTrack). # Accept an invitation Source: https://docs.smokeball.com/api-reference/invitations/accept-an-invitation /openapi.json post /invitations/{id}/accept Server-to-server clients only. Creates the invited person's login under the invited email address, with a user id derived from the invitation, and registers it with the firm; omit `password` to have the identity provider email them a way to set one. Repeating the request, even after the invitation's expiry, answers the same reply and creates the login once. # Cancel an invitation Source: https://docs.smokeball.com/api-reference/invitations/cancel-an-invitation /openapi.json delete /invitations/{id} A cancelled invitation can no longer be accepted. Cancelling is asynchronous: the invitation reads Cancelled once the cancellation is applied. # Get an invitation Source: https://docs.smokeball.com/api-reference/invitations/get-an-invitation /openapi.json get /invitations/{id} A newly created invitation answers 404 for a few seconds after `POST /invitations` returns. # Invite a person to join the firm Source: https://docs.smokeball.com/api-reference/invitations/invite-a-person-to-join-the-firm /openapi.json post /invitations Invites a person who has no login yet to the firm associated with the authenticated client; accepting the invitation creates their login. The invitation is recorded asynchronously, so the returned link answers 404 for a few seconds. Set `sendEmail` to false to deliver it yourself. # Resend an invitation Source: https://docs.smokeball.com/api-reference/invitations/resend-an-invitation /openapi.json post /invitations/{id}/resend Sends no email — the authenticated client delivers the invitation link itself. Resending is asynchronous: once applied, a pending or expired invitation reads Pending and expires seven days later. # Search invitations Source: https://docs.smokeball.com/api-reference/invitations/search-invitations /openapi.json get /invitations Lists the firm's invitations, most recently expiring first. Only pending invitations are returned unless `status` says otherwise. # Get an invoice Source: https://docs.smokeball.com/api-reference/invoices/get-an-invoice /openapi.json get /matters/{matterId}/invoices/{invoiceId} Retrieves a specified invoice. # Get download url for an invoice Source: https://docs.smokeball.com/api-reference/invoices/get-download-url-for-an-invoice /openapi.json get /matters/{matterId}/invoices/{invoiceId}/download Retrieves download details for a specified invoice. # Get invoices Source: https://docs.smokeball.com/api-reference/invoices/get-invoices /openapi.json get /matters/{matterId}/invoices Returns a list of invoices for the specified matter. # Get a layout design Source: https://docs.smokeball.com/api-reference/layout-designs/get-a-layout-design /openapi.json get /layouts/{id} Retrieves a specified layout design. # Get layout designs Source: https://docs.smokeball.com/api-reference/layout-designs/get-layout-designs /openapi.json get /layouts Returns a list of layout designs. # Add a contact to a layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/add-a-contact-to-a-layout /openapi.json post /matters/{matterId}/layouts/{itemId}/contacts Adds a new contact on a specified layout. # Add a new layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/add-a-new-layout /openapi.json post /matters/{matterId}/layouts Adds a new layout matter item to a matter. # Get a layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/get-a-layout /openapi.json get /matters/{matterId}/layouts/{itemId} Retrieves a layout item on a specified matter. # Get layout contacts Source: https://docs.smokeball.com/api-reference/layout-matter-items/get-layout-contacts /openapi.json get /matters/{matterId}/layouts/{itemId}/contacts Returns a list of contacts on a specified layout. # Get layouts Source: https://docs.smokeball.com/api-reference/layout-matter-items/get-layouts /openapi.json get /matters/{matterId}/layouts Returns a collection of layout items on a specified matter. # Merge a layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/merge-a-layout /openapi.json patch /matters/{matterId}/layouts/{itemId} Merges layout data. # Remove a contact from a layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/remove-a-contact-from-a-layout /openapi.json delete /matters/{matterId}/layouts/{itemId}/contacts Removes a contact from a specified layout, deleting the associated role or relationship from the matter. # Remove a layout Source: https://docs.smokeball.com/api-reference/layout-matter-items/remove-a-layout /openapi.json delete /matters/{matterId}/layouts/{itemId} Removes a layout matter item from a matter. # Get an item on a matter Source: https://docs.smokeball.com/api-reference/matter-items/get-an-item-on-a-matter /openapi.json get /matters/{matterId}/items/{id} Returns an item on the specified matter. # Get items on a matter Source: https://docs.smokeball.com/api-reference/matter-items/get-items-on-a-matter /openapi.json get /matters/{matterId}/items Returns a list of items on the specified matter. # Get a matter type Source: https://docs.smokeball.com/api-reference/matter-types/get-a-matter-type /openapi.json get /mattertypes/{id} Retrieves a specified matter type. # Get matter types Source: https://docs.smokeball.com/api-reference/matter-types/get-matter-types /openapi.json get /mattertypes Returns a paginated list of matter types based on search criteria. Deleted matter types are not included. Note: If no `Location` is provided, all matter types from all locations will be returned. # Get matter types categories Source: https://docs.smokeball.com/api-reference/matter-types/get-matter-types-categories /openapi.json get /mattertypes/categories Returns a list of all the possible matter type categories. # Add tags to a matter Source: https://docs.smokeball.com/api-reference/matters/add-tags-to-a-matter /openapi.json post /matters/{matterId}/tags # Create a matter Source: https://docs.smokeball.com/api-reference/matters/create-a-matter /openapi.json post /matters Creates a new matter. Note: This endpoint can also be used to create a `Lead`. When creating a `Lead` you MUST remember to set a valid lead specific `matterTypeId` field (see field description below for more details) and set the `isLead` field to true. The `clientRole` and `otherSideRole` are optional and cannot be identical. They will be set to the default role(s) for the specified matter type - if not provided. # Delete a matter Source: https://docs.smokeball.com/api-reference/matters/delete-a-matter /openapi.json delete /matters/{matterId} Deletes a specified matter. # Get a matter Source: https://docs.smokeball.com/api-reference/matters/get-a-matter /openapi.json get /matters/{matterId} Retrieves a specified matter. # Get matter billing configuration Source: https://docs.smokeball.com/api-reference/matters/get-matter-billing-configuration /openapi.json get /matters/{matterId}/billingconfiguration Retrieves the billing configuration for the specified matter. # Get matter tags Source: https://docs.smokeball.com/api-reference/matters/get-matter-tags /openapi.json get /matters/{matterId}/tags # Get matters Source: https://docs.smokeball.com/api-reference/matters/get-matters /openapi.json get /matters Returns a list of matters. To return leads, set the `isLead` search option to true. # Get the archive for a matter Source: https://docs.smokeball.com/api-reference/matters/get-the-archive-for-a-matter /openapi.json get /matters/{matterId}/archive Retrieves an archive for a matter. # Patch a matter Source: https://docs.smokeball.com/api-reference/matters/patch-a-matter /openapi.json patch /matters/{matterId} Patches a specified matter. Note: This endpoint can also be used to update a `Lead`, convert `Lead` to `Matter` and change the `Lead`/`Matter` type. See `Matters` documentation for more details on how to use this. The following actions are not supported: - `items` cannot be updated where either `clientIds` and/or `otherSideIds` are being updated - `items` cannot be updated when converting a `Lead` to `Matter` or changing the `Lead`/`Matter` type - `clientRole` cannot be updated, unless converting a `Lead` to `Matter` or changing the `Lead`/`Matter` type - `Matter` cannot be converted back to `Lead` - `splitOriginatingStaffSettings` cannot be updated without also providing `originatingStaffIds` in the same PATCH request - `personAssistingStaffIds` cannot contain `personAssistingStaffId` - `originatingStaffIds` cannot contain `originatingStaffId` - If split originating staff settings are enabled, `originatingStaffIds` and `originatingStaffId` cannot be updated - When updating via `items`, client role items and their sub-items must contain at least one item (cannot be empty), while non-client role items and their sub-items can be empty arrays to remove all items # Patch an archive for a matter Source: https://docs.smokeball.com/api-reference/matters/patch-an-archive-for-a-matter /openapi.json patch /matters/{matterId}/archive Updates a specified matter's archive with patch operations. # Patch matter billing configuration Source: https://docs.smokeball.com/api-reference/matters/patch-matter-billing-configuration /openapi.json patch /matters/{matterId}/billingconfiguration Patches the billing configuration for the specified matter. # Remove tags from a matter Source: https://docs.smokeball.com/api-reference/matters/remove-tags-from-a-matter /openapi.json delete /matters/{matterId}/tags/{tagId} Removes the tag from the matter without deleting from the account. # Update a matter Source: https://docs.smokeball.com/api-reference/matters/update-a-matter /openapi.json put /matters/{matterId} Updates a specified matter. The following actions are currently not supported: - `clientIds` cannot be modified - `otherSideIds` cannot be modified - `Matter` cannot be converted back to `Lead` # Update an archive for a matter. Source: https://docs.smokeball.com/api-reference/matters/update-an-archive-for-a-matter /openapi.json put /matters/{matterId}/archive Updates a specified matter's archive # Update matter billing configuration Source: https://docs.smokeball.com/api-reference/matters/update-matter-billing-configuration /openapi.json put /matters/{matterId}/billingconfiguration Updates the billing configuration for the specified matter. # Create a memo on a matter Source: https://docs.smokeball.com/api-reference/memos/create-a-memo-on-a-matter /openapi.json post /matters/{matterId}/memos Creates a memo on the specified matter. # Delete a memo Source: https://docs.smokeball.com/api-reference/memos/delete-a-memo /openapi.json delete /matters/{matterId}/memos/{id} Deletes a specified memo. # Get a memo Source: https://docs.smokeball.com/api-reference/memos/get-a-memo /openapi.json get /matters/{matterId}/memos/{id} Retrieves a specified memo. # Get memos on a matter Source: https://docs.smokeball.com/api-reference/memos/get-memos-on-a-matter /openapi.json get /matters/{matterId}/memos Returns a list of memos on the specified matter. # Update a memo Source: https://docs.smokeball.com/api-reference/memos/update-a-memo /openapi.json put /matters/{matterId}/memos/{id} Updates a specified memo. # Create a notification Source: https://docs.smokeball.com/api-reference/notifications/create-a-notification /openapi.json post /notifications Creates a notification. # Get a notification Source: https://docs.smokeball.com/api-reference/notifications/get-a-notification /openapi.json get /notifications/{id} Retrieves a specified notification. # Create a new plugin Source: https://docs.smokeball.com/api-reference/plugins/create-a-new-plugin /openapi.json post /plugins Creates a new plugin with the specified plugin details. # Delete existing plugin Source: https://docs.smokeball.com/api-reference/plugins/delete-existing-plugin /openapi.json delete /plugins/{pluginId} Deletes an existing plugin by id. # Get a plugin Source: https://docs.smokeball.com/api-reference/plugins/get-a-plugin /openapi.json get /plugins/{pluginId} Returns the plugin. # Get a plugin subscription Source: https://docs.smokeball.com/api-reference/plugins/get-a-plugin-subscription /openapi.json get /plugins/{pluginId}/subscriptions/{id} Returns the plugin subscription. # Get plugin subscriptions Source: https://docs.smokeball.com/api-reference/plugins/get-plugin-subscriptions /openapi.json get /plugins/{pluginId}/subscriptions Returns plugin subscriptions for the account. # Get plugins Source: https://docs.smokeball.com/api-reference/plugins/get-plugins /openapi.json get /plugins Returns existing plugins. # Request plugin URL Source: https://docs.smokeball.com/api-reference/plugins/request-plugin-url /openapi.json post /plugins/{pluginId}/url Requests a secure URL from the plugin's endpoint for invoking the plugin. Accepts additional key/value pairs to pass to the plugin endpoint. Reserved keys (AccountId, UserId, UserEmail, MatterId, AppId, EmbedToken) cannot be included in AdditionalData. # Subscribe account to plugin Source: https://docs.smokeball.com/api-reference/plugins/subscribe-account-to-plugin /openapi.json post /plugins/{pluginId}/subscriptions Subscribes an account with the specified plugin. # Unsubscribes account from plugin Source: https://docs.smokeball.com/api-reference/plugins/unsubscribes-account-from-plugin /openapi.json delete /plugins/{pluginId}/subscriptions/{id} Unsubscribes an account from the specified plugin. # Update existing plugin Source: https://docs.smokeball.com/api-reference/plugins/update-existing-plugin /openapi.json put /plugins/{pluginId} Updates a plugin with the specified plugin details. # Creates a portal task Source: https://docs.smokeball.com/api-reference/portal/creates-a-portal-task /openapi.json post /matters/{matterId}/portal/tasks Creates a new client portal task associated with the specified matter. # Patch a portal task Source: https://docs.smokeball.com/api-reference/portal/patch-a-portal-task /openapi.json patch /matters/{matterId}/portal/tasks/{taskId} Updates a client portal task. # Sends a portal message Source: https://docs.smokeball.com/api-reference/portal/sends-a-portal-message /openapi.json post /matters/{matterId}/portal/messages Sends a message to the specified contact on the client portal. When using the Client Credentials grant type, include the UserId header to impersonate the lawyer in the firm who is sending the message. # Get a referral type Source: https://docs.smokeball.com/api-reference/referral-types/get-a-referral-type /openapi.json get /referraltypes/{id} Retrieves a specified referral type. # Get referral types Source: https://docs.smokeball.com/api-reference/referral-types/get-referral-types /openapi.json get /referraltypes Returns referral types, including custom referral types. # Add relationship to a role Source: https://docs.smokeball.com/api-reference/relationships/add-relationship-to-a-role /openapi.json post /matters/{matterId}/roles/{roleId}/relationships Appends a new relationship with an existing role in the specified matter. # Get relationship on a role Source: https://docs.smokeball.com/api-reference/relationships/get-relationship-on-a-role /openapi.json get /matters/{matterId}/roles/{roleId}/relationships/{id} Returns a relationship with a specified role in a matter. # Get relationships on a matter Source: https://docs.smokeball.com/api-reference/relationships/get-relationships-on-a-matter /openapi.json get /matters/{matterId}/roles/{roleId}/relationships Returns associated relationships for a specified matter and role group. # Remove relationship from a role in a matter. Source: https://docs.smokeball.com/api-reference/relationships/remove-relationship-from-a-role-in-a-matter /openapi.json delete /matters/{matterId}/roles/{roleId}/relationships/{id} Removes a relationship from a role in a matter. # Update a relationship Source: https://docs.smokeball.com/api-reference/relationships/update-a-relationship /openapi.json put /matters/{matterId}/roles/{roleId}/relationships/{id} Updates a specified relationship on a matter. # Add role to a matter Source: https://docs.smokeball.com/api-reference/roles/add-role-to-a-matter /openapi.json post /matters/{matterId}/roles Appends a new role in the specified matter. # Get role on a matter Source: https://docs.smokeball.com/api-reference/roles/get-role-on-a-matter /openapi.json get /matters/{matterId}/roles/{id} Returns a role in a specified matter. # Get roles on a matter Source: https://docs.smokeball.com/api-reference/roles/get-roles-on-a-matter /openapi.json get /matters/{matterId}/roles Returns associated roles for a specified matter. # Remove role from a matter Source: https://docs.smokeball.com/api-reference/roles/remove-role-from-a-matter /openapi.json delete /matters/{matterId}/roles/{id} Removes a specified role from a matter. # Update role on a matter Source: https://docs.smokeball.com/api-reference/roles/update-role-on-a-matter /openapi.json put /matters/{matterId}/roles/{id} Updates a specified role on a matter. # Search files with advanced filters Source: https://docs.smokeball.com/api-reference/search/search-files-with-advanced-filters /openapi.json post /search/files Performs a full-account file search and returns files matching the provided query, paging options and filters. # Create firm staff member Source: https://docs.smokeball.com/api-reference/staff/create-firm-staff-member /openapi.json post /staff Creates a staff member in the firm associated with the authenticated client. # Deletes a firm staff member Source: https://docs.smokeball.com/api-reference/staff/deletes-a-firm-staff-member /openapi.json delete /staff/{id} Sets the staff member from the firm associated with the authenticated client as a former staff member. The staff member is set as a former staff member and if they are a user, becomes a former user. User access is also disabled. # Get firm staff member Source: https://docs.smokeball.com/api-reference/staff/get-firm-staff-member /openapi.json get /staff/{id} Retrieves a staff member (based on staff id parameter provided) in the firm associated with the authenticated client. # Search firm staff members Source: https://docs.smokeball.com/api-reference/staff/search-firm-staff-members /openapi.json get /staff Retrieves a paginated list of staff members (filtered based on search parameters provided) in the firm associated with the authenticated client. # Update firm staff member Source: https://docs.smokeball.com/api-reference/staff/update-firm-staff-member /openapi.json put /staff/{id} Updates a staff member in the firm associated with the authenticated client. # Get a matter stage information Source: https://docs.smokeball.com/api-reference/stages/get-a-matter-stage-information /openapi.json get /matters/{matterId}/stages Returns the specified matters stage information. # Get a stage on a stage set Source: https://docs.smokeball.com/api-reference/stages/get-a-stage-on-a-stage-set /openapi.json get /stagesets/{stageSetId}/stages/{id} Return the specified stage on the stage set. # Get a stage set Source: https://docs.smokeball.com/api-reference/stages/get-a-stage-set /openapi.json get /stagesets/{id} Returns the specified stage set. # Get stage sets for the firm Source: https://docs.smokeball.com/api-reference/stages/get-stage-sets-for-the-firm /openapi.json get /stagesets Returns a list of stage sets for the firm. # Get stage to matter mappings in the firm Source: https://docs.smokeball.com/api-reference/stages/get-stage-to-matter-mappings-in-the-firm /openapi.json get /stages Returns a list of stage to matter mappings in the firm. # Create a subtask Source: https://docs.smokeball.com/api-reference/tasks/create-a-subtask /openapi.json post /tasks/{taskId}/subtasks Creates a subtask for a specified task. # Create a task Source: https://docs.smokeball.com/api-reference/tasks/create-a-task /openapi.json post /tasks Creates a task. # Create a task document Source: https://docs.smokeball.com/api-reference/tasks/create-a-task-document /openapi.json post /tasks/{taskId}/documents Links the specified document to the specified task. # Delete a subtask Source: https://docs.smokeball.com/api-reference/tasks/delete-a-subtask /openapi.json delete /tasks/{taskId}/subtasks/{id} Deletes a specified subtask of a specified task. # Delete a task Source: https://docs.smokeball.com/api-reference/tasks/delete-a-task /openapi.json delete /tasks/{id} Deletes a specified task. # Delete a task document Source: https://docs.smokeball.com/api-reference/tasks/delete-a-task-document /openapi.json delete /tasks/{taskId}/documents/{id} Unlinks the specified document from the specified task. # Get a subtask Source: https://docs.smokeball.com/api-reference/tasks/get-a-subtask /openapi.json get /tasks/{taskId}/subtasks/{id} Retrieves a specified subtask. # Get a task Source: https://docs.smokeball.com/api-reference/tasks/get-a-task /openapi.json get /tasks/{id} Retrieves a specified task. # Get a task document Source: https://docs.smokeball.com/api-reference/tasks/get-a-task-document /openapi.json get /tasks/{taskId}/documents/{id} Retrieves the specified task document for the specified task. # Get subtasks Source: https://docs.smokeball.com/api-reference/tasks/get-subtasks /openapi.json get /tasks/{taskId}/subtasks Returns a list of subtasks for a specified task if associated with the authenticated client. # Get task documents Source: https://docs.smokeball.com/api-reference/tasks/get-task-documents /openapi.json get /tasks/{taskId}/documents Returns a list of document links for the specified task, if associated with the authenticated client. # Get tasks Source: https://docs.smokeball.com/api-reference/tasks/get-tasks /openapi.json get /tasks Returns a list of tasks. # Update a subtask Source: https://docs.smokeball.com/api-reference/tasks/update-a-subtask /openapi.json put /tasks/{taskId}/subtasks/{id} Updates a specified subtask of a specified task. # Update a task Source: https://docs.smokeball.com/api-reference/tasks/update-a-task /openapi.json put /tasks/{id} Updates a specified task. # Create a user Source: https://docs.smokeball.com/api-reference/users/create-a-user /openapi.json post /users Creates a user. Provider decides where the user is created. When omitted, Default is used. - Default (the service's default identity provider for new users) - Auth0 InitialPassword decides how the user gets their first password. When omitted, a missing password means Invitation (the identity provider emails the user the way to set their own) and a supplied password is Permanent. Temporary must be changed at first sign-in. Nothing is emailed for Permanent or Temporary - the caller delivers the password. To add an invited person, accept their invitation instead; creating the login here registers them without it. # Get a user Source: https://docs.smokeball.com/api-reference/users/get-a-user /openapi.json get /users/{id} Retrieves a specified user. # Remove a user Source: https://docs.smokeball.com/api-reference/users/remove-a-user /openapi.json delete /users/{id} Removes a user. User must belong to the specified account. # Resend sign-in credentials Source: https://docs.smokeball.com/api-reference/users/resend-sign-in-credentials /openapi.json post /users/{id}/invite Sends a new temporary password to an existing login. It does not invite a person to join a firm. The user must exist and belong to the specified account when specifying id. Optionally resend credentials using an email address instead of a user id. Use a default id of all zeros when posting an email address payload. Email address credential resends are only available to client credential grants. # Create new webhook subscription Source: https://docs.smokeball.com/api-reference/webhooks/create-new-webhook-subscription /openapi.json post /webhooks Creates a new webhook subscription with the specified subscription details. # Deletes a webhook subscription Source: https://docs.smokeball.com/api-reference/webhooks/deletes-a-webhook-subscription /openapi.json delete /webhooks/{id} Deletes and unsubscribes from the specified webhook subscription. # Get a webhook subscription Source: https://docs.smokeball.com/api-reference/webhooks/get-a-webhook-subscription /openapi.json get /webhooks/{id} Returns the webhook subscription. # Get event types Source: https://docs.smokeball.com/api-reference/webhooks/get-event-types /openapi.json get /webhooks/types Returns the available webhook event types. # Get webhook subscriptions Source: https://docs.smokeball.com/api-reference/webhooks/get-webhook-subscriptions /openapi.json get /webhooks Returns existing webhook subscriptions. # Notify a webhook subscription Source: https://docs.smokeball.com/api-reference/webhooks/notify-a-webhook-subscription /openapi.json post /webhooks/{id}/notify Notifies a webhook subscription with a 'noop' event type. Used for testing purposes. # Update a webhook subscription Source: https://docs.smokeball.com/api-reference/webhooks/update-a-webhook-subscription /openapi.json put /webhooks/{id} Updates a webhook subscription with the specified subscription details. # Layouts Source: https://docs.smokeball.com/docs/api-docs/0d3adba1494ce-layouts For context on how layouts fit within matters, see [Matters documentation](/docs/api-docs/voyymdyjn5yp3-matters#layouts). Layouts are User Interfaces that look different from each other, and are designed for a specific/unique purpose. They are designed and maintained by our product team. Each user interface has different fields pertinent to their unique purpose. For example, we have a **Property Details** Layout for **Conveyancing** Matter Types: Or, **Case Details** Layout for some **Litigation** Matter Types: ## Data Mapping How to get a list of layout fields that you can set up a mapping for: 1. Get a reference for the layout design ID by querying a matter type. ```plaintext theme={"dark"} GET https://api.smokeball.com.au/mattertypes/{matterTypeId} ``` Example response: ```json theme={"dark"} { "href": "https://api.smokeball.com.au/mattertypes/9c6f59b6-3871-4fbd-a508-8fc43f03c6b4", "items": [ { "href": "https://api.smokeball.com.au/layouts/b571beb4-9175-436c-a70e-e70acb0f4ed2", "rel": "layouts", "id": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "providerId": "LayoutProvider", "name": "Workers Comp Details" } ], "id": "9c6f59b6-3871-4fbd-a508-8fc43f03c6b4", "versionId": "1bd57eaa-089c-4a92-8136-aed74bf5c389", "name": "Workers Compensation", "category": "Personal Injury", "representativeOptions": [ "Worker", "Employer" ], "location": "NSW" } ``` In this example, `/items/0/id` contains the layout design ID and `/items/0/href` contains a full URL to use in the next step. 2. Retrieve the full list of fields for the given layout design. ```plaintext theme={"dark"} GET https://api.smokeball.com/layouts/{layoutDesignId} ``` Example response: ```json theme={"dark"} { "id": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "href": "https://api.smokeball.com.au/layouts/b571beb4-9175-436c-a70e-e70acb0f4ed2", "fields": [ { "name": "Matter/WorkersCompDetails/BodyPartInjured", "type": "Text" }, { "name": "Matter/WorkersCompDetails/DateOfInjury", "type": "DateTime" }, { "name": "Matter/WorkersCompDetails/DescriptionOfInjury", "type": "Text" } ], "name": "Workers Comp Details" } ``` In this example, `/fields/*/name` contain the layout fields that you can set up a mapping for. ## Working with Layout Data Once you've discovered the available layout designs, you can retrieve, create, update, and delete actual layout data on matters. ### Retrieving All Layouts on a Matter Get all layout items that exist on a matter, including both top-level layouts and sub-items. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" } ``` Example response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "items": [ { "id": "a1b2c3d4-1234-5678-9abc-def012345678", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678", "itemId": "a1b2c3d4-1234-5678-9abc-def012345678", "index": 0, "parentItemId": null, "layoutDesign": { "id": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "href": "https://api.smokeball.com/layouts/b571beb4-9175-436c-a70e-e70acb0f4ed2", "rel": "Layouts", "name": "Workers Comp Details" }, "values": [ { "key": "Matter/WorkersCompDetails/BodyPartInjured", "value": "Lower back" }, { "key": "Matter/WorkersCompDetails/DateOfInjury", "value": "2024-06-15T14:30:00.0000000Z" } ] }, { "id": "e5f6g7h8-5678-9012-3def-456789012345", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/e5f6g7h8-5678-9012-3def-456789012345", "itemId": "e5f6g7h8-5678-9012-3def-456789012345", "index": 0, "parentItemId": "d9e8f7a6-4321-8765-bcde-f123456789ab", "layoutDesign": { "id": "c8d9e0f1-2345-6789-0abc-123456789def", "href": "https://api.smokeball.com/layouts/c8d9e0f1-2345-6789-0abc-123456789def", "rel": "Layouts", "name": "Loan Details" }, "values": [] } ] } ``` * **Top-level layouts** have `parentItemId: null` (e.g., Workers Comp Details) * **Sub-item layouts** have a `parentItemId` referencing their parent layout (e.g., Loan Details under a Lender contact) ### Retrieving a Specific Layout Item Get detailed information about a single layout item, including all its field values. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678" } ``` Example response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "id": "a1b2c3d4-1234-5678-9abc-def012345678", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678", "itemId": "a1b2c3d4-1234-5678-9abc-def012345678", "index": 0, "parentItemId": null, "layoutDesign": { "id": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "href": "https://api.smokeball.com/layouts/b571beb4-9175-436c-a70e-e70acb0f4ed2", "rel": "Layouts" }, "values": [ { "key": "Matter/WorkersCompDetails/BodyPartInjured", "value": "Lower back" }, { "key": "Matter/WorkersCompDetails/DateOfInjury", "value": "2024-06-15T14:30:00.0000000Z" }, { "key": "Matter/WorkersCompDetails/DescriptionOfInjury", "value": "Injury occurred while lifting heavy equipment" } ] } ``` ## Creating Layout Items Add new layout data to a matter. This operation is asynchronous and returns a `202 Accepted` response. ### Adding a Top-Level Layout Create a new layout item at the matter level with initial field values. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "layoutDesignId": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "values": [ { "key": "Matter/WorkersCompDetails/BodyPartInjured", "value": "Lower back" }, { "key": "Matter/WorkersCompDetails/DateOfInjury", "value": "2024-06-15T14:30:00.0000000Z" } ] }' ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts", "headers": { "Content-Type": "application/json" }, "body": { "layoutDesignId": "b571beb4-9175-436c-a70e-e70acb0f4ed2", "values": [ { "key": "Matter/WorkersCompDetails/BodyPartInjured", "value": "Lower back" }, { "key": "Matter/WorkersCompDetails/DateOfInjury", "value": "2024-06-15T14:30:00.0000000Z" } ] } } ``` Example response: ```json theme={"dark"} HTTP/1.1 202 Accepted Content-Type: application/json { "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678", "rel": "Layouts" } ``` ### Adding a Sub-Layout Item Create a nested layout item under a parent layout by including the `parentItemId`. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "layoutDesignId": "c8d9e0f1-2345-6789-0abc-123456789def", "parentItemId": "d9e8f7a6-4321-8765-bcde-f123456789ab", "values": [ { "key": "Matter/LoanDetails/LoanAmount", "value": "350000.00" }, { "key": "Matter/LoanDetails/InterestRate", "value": "5.25" } ] }' ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts", "headers": { "Content-Type": "application/json" }, "body": { "layoutDesignId": "c8d9e0f1-2345-6789-0abc-123456789def", "parentItemId": "d9e8f7a6-4321-8765-bcde-f123456789ab", "values": [ { "key": "Matter/LoanDetails/LoanAmount", "value": "350000.00" }, { "key": "Matter/LoanDetails/InterestRate", "value": "5.25" } ] } } ``` ### Understanding Async Operations Layout creation and update operations are asynchronous and return a `202 Accepted` response with a link to the resource being created or updated. **Best Practices:** * Use the returned `href` to verify the operation completed successfully * Implement retry logic for failed requests * Consider using webhooks for completion notifications instead of polling * Handle `503 Service Unavailable` responses with exponential backoff ## Updating Layout Data Update field values on an existing layout item. This is a partial update operation - only the fields you specify will be changed. ```bash curl theme={"dark"} curl --request PATCH "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "values": [ { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/Note", "value": "Updated settlement offer received" }, { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/OfferDate", "value": "2024-07-20T09:15:00.0000000Z" } ] }' ``` ```json http theme={"dark"} { "method": "patch", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1", "headers": { "Content-Type": "application/json" }, "body": { "values": [ { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/Note", "value": "Updated settlement offer received" }, { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/OfferDate", "value": "2024-07-20T09:15:00.0000000Z" } ] } } ``` Example response: ```json theme={"dark"} HTTP/1.1 202 Accepted Content-Type: application/json { "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1", "rel": "Layouts" } ``` The PATCH operation merges your changes with existing data. Only include the fields you want to update - other fields remain unchanged. ## Deleting Layout Items Remove a layout item from a matter. This operation is asynchronous and cannot be undone via the API. ```bash curl theme={"dark"} curl --request DELETE "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "delete", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/a1b2c3d4-1234-5678-9abc-def012345678" } ``` Example response: ```json theme={"dark"} HTTP/1.1 202 Accepted ``` **Important considerations:** * Deletion is asynchronous (202 response) * Deleting a parent layout may impact sub-items * Deleted layouts cannot be recovered via the API ## Field Type Reference Each layout field has a specific type that determines how values should be formatted. Always retrieve the layout design first to understand field types before setting values. ### Text Fields Simple string values for text-based fields. **Example:** ```json theme={"dark"} { "key": "Matter/WorkersCompDetails/DescriptionOfInjury", "value": "Injury occurred while lifting heavy equipment" } ``` ### DateTime Fields Date and time values must use ISO 8601 format in UTC timezone. **Format:** `YYYY-MM-DD` **Example:** ```json theme={"dark"} { "key": "Matter/WorkersCompDetails/DateOfInjury", "value": "2024-06-15" } ``` ### Combobox Fields Combobox fields must use a value from the predefined `possibleValues` list in the layout design. **Two-step process:** 1. Get the layout design to see allowed values: ```json theme={"dark"} { "name": "Matter/PropertyDetails/PropertyType", "type": "Combobox", "possibleValues": [ "Residence", "Commercial", "Factory", "Office" ] } ``` 2. Use one of the allowed values: ```json theme={"dark"} { "key": "Matter/PropertyDetails/PropertyType", "value": "Residence" } ``` Values are case-sensitive and must exactly match one of the `possibleValues`. Using an invalid value will result in a validation error. ### Checkbox Fields Boolean values represented as strings. **Allowed values:** `"true"` or `"false"` **Example:** ```json theme={"dark"} { "key": "Matter/CaseDetails/IsRushOrder", "value": "true" } ``` ```json theme={"dark"} { "key": "Matter/PropertyDetails/RequiresNotarization", "value": "false" } ``` ### Number Fields Numeric values represented as strings. **Examples:** ```json theme={"dark"} { "key": "Matter/SettlementNegotiations/ClaimAmount", "value": "125000.50" } ``` ```json theme={"dark"} { "key": "Matter/PropertyDetails/NumberOfBedrooms", "value": "3" } ``` ## Working with Contacts on Layouts Associate contacts with specific layout items (e.g., linking a lender contact to a loan details layout). ### Getting Contacts on a Layout Retrieve all contacts associated with a layout item. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/e5f6g7h8-5678-9012-3def-456789012345/contacts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/e5f6g7h8-5678-9012-3def-456789012345/contacts" } ``` Example response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json [ { "contactId": "1a2b3c4d-5678-90ab-cdef-123456789012", "key": "Matter/LoanDetails/Lender", } ] ``` ### Adding Contacts to a Layout Associate a contact with a layout item. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/e5f6g7h8-5678-9012-3def-456789012345/contacts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "contactId": "1a2b3c4d-5678-90ab-cdef-123456789012", "key": "Matter/LoanDetails/Lender" }' ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/e5f6g7h8-5678-9012-3def-456789012345/contacts", "headers": { "Content-Type": "application/json" }, "body": { "contactId": "1a2b3c4d-5678-90ab-cdef-123456789012", "key": "Matter/LoanDetails/Lender" } } ``` If the specified `key` already references a contact, remove the existing role or relationship manually before adding a new contact. ## Common Patterns & Best Practices ### End-to-End Workflow: Property Purchase Complete example for creating and populating a Property Details layout in a conveyancing matter: **1. Find the matter type and layout design** ```bash theme={"dark"} GET /mattertypes/{matterTypeId} ``` **2. Get field definitions from the layout design** ```bash theme={"dark"} GET /layouts/{layoutDesignId} ``` **3. Create the layout with initial data** ```bash theme={"dark"} POST /matters/{matterId}/layouts { "layoutDesignId": "property-details-layout-id", "values": [ { "key": "Matter/PropertyDetails/Address", "value": "123 Main Street, Sydney NSW 2000" }, { "key": "Matter/PropertyDetails/PropertyType", "value": "Residence" } ] } ``` **4. Update as information becomes available** ```bash theme={"dark"} PATCH /matters/{matterId}/layouts/{layoutItemId} { "values": [ { "key": "Matter/PropertyDetails/PurchasePrice", "value": "750000.00" }, { "key": "Matter/PropertyDetails/SettlementDate", "value": "2024-09-30T14:00:00.0000000Z" } ] } ``` ### Error Handling Strategies **Validate before sending:** * Check field types against layout design * Validate DateTime format matches ISO 8601 * Confirm Combobox values are in possibleValues **Handle common errors:** * `400 Bad Request`: Validation error - check field types and formats * `404 Not Found`: Layout design or matter doesn't exist * `503 Service Unavailable`: Implement retry with exponential backoff **Use webhooks for async operations:** Instead of polling the returned `href`, configure webhooks to receive notifications when operations complete. # Introduction Source: https://docs.smokeball.com/docs/api-docs/1e13a13124aee-introduction The Smokeball API conforms to [REST](https://en.wikipedia.org/wiki/Representational_state_transfer). Our API has predictable resource-oriented URLS, accepts [JSON](https://www.json.org/json-en.html) request bodies, returns [JSON](https://www.json.org/json-en.html) and uses standard [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) status codes and verbs. If you've developed a software solution for the Legal Industry, an opportunity may exist to work with Smokeball to help our clients 'Run Their Best Firm'. To start your integration journey with Smokeball, please follow this [link](https://marketplace.smokeball.com/application_forms/join-the-smokeball-partner-program/partner_applications/new) to complete the registration of interest form, and a member from our Partnerships or Product Teams will be in touch. ## Getting Started The Smokeball API provides a comprehensive set of endpoints for integrating with Smokeball's practice management platform. Whether you're building custom integrations, automating workflows, or extending Smokeball's functionality, this API documentation will guide you through the process. ### Key Concepts Before you begin, familiarize yourself with these essential concepts: * **Base URLs**: Different URLs for authentication and API requests, varying by region and environment * **Authentication**: OAuth 2.0-based authentication flow for secure access * **Rate Limits**: Request throttling to ensure consistent platform performance * **Request Format**: JSON request/response format with standard HTTP methods ## Next Steps Follow these steps to get started with the Smokeball API: 1. **[Base URLs](/docs/api-docs/z2q8vla6b77g8-base-urls)** - Configure the correct endpoints for your region and environment 2. **[Authentication](/docs/api-docs/c916c683c136e-authentication-overview)** - Set up OAuth 2.0 authentication to access the API 3. **[Rate Limits](/docs/api-docs/vj4jovpoopdov-rate-limits)** - Understand throttling policies and best practices 4. **[Making Requests](/docs/api-docs/ivmfjbc61tvjs-making-requests)** - Learn how to structure and send API requests Make sure to use the correct base URLs for your target region (US, AU, or UK) and environment (Production or Staging). # Searching Source: https://docs.smokeball.com/docs/api-docs/272770a37d359-searching Search for data ## Overview Some endpoints will have a query parameter called `search` *(array\[\[string]])* which allows you to specify multiple search terms. These terms will be combined using an **AND** operator. At the moment, there is no support for other logical operators. An API endpoint that supports searching will specify in its documentation which fields are searchable. The description will list all the fields that can be used with the search parameter, such as 'email', 'phone', 'name', etc. This **GET** Contacts endpoint supports searching for *email*, *phone*, and *name*. ## Syntax The basic syntax for a search term is: ```plaintext theme={"dark"} search=\[field name\]\[operator\]\[value\] ``` The following operators are supported: | Operator | Description | | - | - | | : | Equals | | :! | Not equal to | | :> | Greater than | | :>= | Greater than or equal | | :\< | Less than | | :\<= | Less than or equal | #### Wildcards When searching for string values, the wildcard character `*` can be used: | Example term | Description | | - | - | | john\* | Will search the field for values *starting* with 'john' | | \*john | Will search the field for values *ending* with 'john' | | \*john\* | Will search the field for values *containing* with 'john' | All string searches are case insensitive so searching for 'john' will also match with 'John' #### Examples If you wanted to search for a contact whose name contains 'Steele' the query would be: ```plaintext theme={"dark"} GET ./contacts?search=name:\*Steele\* ``` If you wanted to find results where the name contains 'Steele' but not 'Hunter' the query would be: ```plaintext theme={"dark"} GET ./contacts?search=name:\*Steele\*&search=name:!\*Hunter\* ``` The search terms are combined with the AND operator by default and can not be changed at this time. # Authorization Code Grant Source: https://docs.smokeball.com/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant Use the Authorization Code Grant to authenticate and authorize users The Authorization Code Grant is a standard OAuth 2.0 flow that enables secure user authentication and authorization. This flow allows applications to obtain an **access\_token** by first receiving an authorization code after the user logs in. The process works as follows: 1. Your application redirects the user to authenticate with their Smokeball credentials 2. Upon successful authentication, Smokeball returns an authorization code to your redirect URI 3. Your application exchanges this code for an **access\_token** that can be used to make authenticated API requests This is the recommended OAuth 2.0 flow when you need to access the Smokeball API on behalf of a specific user. ## Request User Authorization Direct the user to `https://auth.smokeball.com/oauth2/authorize` with the following parameters: Example: ```bash curl theme={"dark"} curl -X GET 'https://auth.smokeball.com/oauth2/authorize?response_type=code&client_id=xxxxx&redirect_uri=https://xxxxxxxx&scope=contacts%2Fread%20matters%2Fread%20matters%2Fwrite' ``` ```json http theme={"dark"} { "method": "GET", "url": "https://auth.smokeball.com/oauth2/authorize", "query": { "response_type": "code", "client_id": "xxxxx", "redirect_uri": "https://xxxxxxxx", "scope": "contacts/read matters/read matters/write" } } ``` Must be set to "code" for Authorization Code flow. Determines the OAuth 2.0 grant type being requested. The client identifier issued during application registration. Used to identify the application making the authorization request. The URI where the authorization server will send the authorization code. Must match one of the pre-registered redirect URIs. An opaque value used to maintain state between request and callback. Helps prevent CSRF attacks in the OAuth flow. This parameter is optional. The [scopes](/docs/api-docs/qf83nz2scopes-scopes) of access being requested, as a space-delimited list (for example `contacts/read matters/read matters/write`). Every scope requested must be assigned to your app in the Developer Console. OAuth 2.0 expects a client to state the scopes it wants, so always include this parameter. The install URL shown on your app's page in the Developer Console already includes every scope assigned to the app, sorted alphabetically, and updates as scopes are added or removed. If the parameter is omitted the authorization server currently falls back to issuing a token with all scopes assigned to the app, but you should not rely on this behaviour. The user will be directed to a login page where they will need to use their Smokeball credentials to login. If authorized, the user will be redirected to: ```html theme={"dark"} https://your_redirect_uri?code=xxxxx ``` The response returns a one time use code that is valid for five minutes. > See [AWS Cognito Authorization Endpoint Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html) for more information. ## Request an Access Token Once you have obtained an authorization code, you can now use it to get an **access\_token** which may be used to make requests to the API. Make a POST request to `https://auth.smokeball.com/oauth2/token` with the following parameters and headers: ```bash curl theme={"dark"} curl --request POST \ --url https://auth.smokeball.com/oauth2/token \ --header "Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=" \ --header "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "client_id=xxxx" \ --data-urlencode "redirect_uri=https://xxxxx" \ --data-urlencode "code=the code acquired from step 1" ``` ```json http theme={"dark"} { "method": "POST", "url": "https://auth.smokeball.com/oauth2/token", "headers": { "Authorization": "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=", "Content-Type": "application/x-www-form-urlencoded" }, "body": { "grant_type": "authorization_code", "client_id": "xxxx", "redirect_uri": "https://xxxxx", "code": "the code acquired from step 1" } } ``` > The **Authorization** header must be the string "Basic" followed by your client\_id and client\_secret with a colon : in between, Base64 Encoded. > For example, client\_id:client\_secret is Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ= **Sample Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "refresh_token":"dn43ud8uj32nk2je", "access_token":"dmcxd329ujdmkemkd349r", "token_type":"Bearer", "expires_in":3600 } ``` A token used to obtain a new access token when the current one expires. By default, the returned **refresh\_token** is valid for 180 days. If you require a longer expiration period, please contact us to discuss your specific needs. The 180-day expiry took effect on **24 August 2026** and applies to refresh tokens issued on or after that date. Refresh tokens issued before then remain valid for 30 days. The access token used to authenticate API requests. By default, the returned **access\_token** is valid for 60 minutes. The type of token issued (typically `"Bearer"`). Indicates how the token should be presented in API requests. The lifetime of the access token in **seconds**. After expiration, a new token must be obtained. > **Important**: The refresh token expiry is not returned in the above response. Your application is responsible for monitoring the expiration of the refresh token and prompting the user to re-authenticate when it expires. This ensures continuous access to the application without interruptions. See [AWS Cognito Token Endpoint Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html) for more information The use of **id\_token** to make API requests has been deprecated. API requests can now also be made using the **access\_token**. For further information, see [migration section](#migrating-from-id-token-to-access-token) below for migrating from **id\_token** to **access\_token**. Please note that the **id\_token** will continue to work for the time being and **no immediate action is required**. ## PKCE (Proof Key for Code Exchange) PKCE is an extension of the Authorization Code Grant flow, designed for mobile and single-page applications where storing a client secret securely is difficult. It ensures a more secure flow by requiring a dynamically generated code verifier and code challenge. It is recommended that all authorization code flows are authorized using PKCE and it is *required* for all public api clients, which do not have a client secret. ### Generate a Code Verifier and Code Challenge To initiate the PKCE flow, you must generate a **code\_verifier**, which is a random string, and a **code\_challenge**, which is derived by hashing the **code\_verifier** using SHA-256 and then Base64-URL encoding it. ```csharp c# theme={"dark"} using System; using System.Security.Cryptography; using System.Text; class PKCEGenerator { public static void Main() { string codeVerifier = GenerateCodeVerifier(); string codeChallenge = GenerateCodeChallenge(codeVerifier); Console.WriteLine("Code Verifier: " + codeVerifier); Console.WriteLine("Code Challenge: " + codeChallenge); } // Generate a random code verifier private static string GenerateCodeVerifier() { var randomBytes = new byte[32]; // 32-byte random string using (var rng = RandomNumberGenerator.Create()) { rng.GetBytes(randomBytes); } return Base64UrlEncode(randomBytes); } // Create code challenge using SHA256 private static string GenerateCodeChallenge(string codeVerifier) { using (var sha256 = SHA256.Create()) { byte[] challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier)); return Base64UrlEncode(challengeBytes); } } // Base64 URL Encoding (removes padding, uses '-' and '_') private static string Base64UrlEncode(byte[] bytes) { return Convert.ToBase64String(bytes) .Replace("+", "-") .Replace("/", "_") .TrimEnd('='); } } ``` ```javascript javascript theme={"dark"} // Function to generate a random code verifier function generateCodeVerifier() { const array = new Uint8Array(32); // 32-byte random string window.crypto.getRandomValues(array); return base64UrlEncode(array); } // Function to create a code challenge from the code verifier using SHA256 async function generateCodeChallenge(codeVerifier) { const encoder = new TextEncoder(); const data = encoder.encode(codeVerifier); const digest = await crypto.subtle.digest('SHA-256', data); return base64UrlEncode(new Uint8Array(digest)); } // Function to Base64 URL encode (removes padding, uses '-' and '_') function base64UrlEncode(arrayBuffer) { let base64 = btoa(String.fromCharCode.apply(null, arrayBuffer)) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); return base64; } // Example usage (async () => { const codeVerifier = generateCodeVerifier(); const codeChallenge = await generateCodeChallenge(codeVerifier); console.log('Code Verifier:', codeVerifier); console.log('Code Challenge:', codeChallenge); })(); ``` ### Request Authorization Code for Access Token Direct the user to `https://auth.smokeball.com/oauth2/authorize` with the following parameters: ```bash curl theme={"dark"} curl --request GET \ --url "https://auth.smokeball.com/oauth2/authorize?response_type=code&client_id=xxxxx&redirect_uri=https://your_redirect_uri&scope=contacts%2Fread%20matters%2Fread%20matters%2Fwrite&code_challenge_method=S256&code_challenge=your_code_challenge" ``` ```json http theme={"dark"} { "method": "GET", "url": "https://auth.smokeball.com/oauth2/authorize", "query": { "response_type": "code", "client_id": "xxxxx", "redirect_uri": "https://your_redirect_uri", "scope": "contacts/read matters/read matters/write", "code_challenge_method": "S256", "code_challenge": "your_code_challenge" } } ``` Must be set to "code" for Authorization Code flow. Determines the OAuth 2.0 grant type being requested. The client identifier issued during application registration. Used to identify the application making the authorization request. The URI where the authorization server will send the authorization code. Must match one of the pre-registered redirect URIs. The PKCE code challenge derived from the code verifier. Generated using the S256 code challenge method. The hash method used to generate the code challenge (must be "S256"). Ensures secure PKCE implementation for authorization flow. An opaque value used to maintain state between request and callback. Helps prevent CSRF attacks in the OAuth flow. This parameter is optional. The [scopes](/docs/api-docs/qf83nz2scopes-scopes) of access being requested, as a space-delimited list (for example `contacts/read matters/read matters/write`). Every scope requested must be assigned to your app in the Developer Console. OAuth 2.0 expects a client to state the scopes it wants, so always include this parameter. The install URL shown on your app's page in the Developer Console already includes every scope assigned to the app, sorted alphabetically, and updates as scopes are added or removed. If the parameter is omitted the authorization server currently falls back to issuing a token with all scopes assigned to the app, but you should not rely on this behaviour. For Smokeball API, only `S256` method is allowed for `code_challenge_method` The user will be directed to the login page, and if authorized, redirected back with a code. ### Exchange Authorization Code for Access Token After receiving the authorization code, use it along with the code\_verifier to request an access token: ```bash curl theme={"dark"} curl --request POST \ --url https://auth.smokeball.com/oauth2/token \ --header "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "client_id=xxxxx" \ --data-urlencode "code=authorization_code_from_previous_step" \ --data-urlencode "redirect_uri=https://your_redirect_uri" \ --data-urlencode "code_verifier=your_code_verifier" ``` ```json http theme={"dark"} { "method": "POST", "url": "https://auth.smokeball.com/oauth2/token", "headers": { "Content-Type": "application/x-www-form-urlencoded" }, "body": { "grant_type": "authorization_code", "client_id": "xxxxx", "code": "authorization_code_from_previous_step", "redirect_uri": "https://your_redirect_uri", "code_verifier": "your_code_verifier" } } ``` Must be set to "authorization\_code" for this flow. Specifies the OAuth 2.0 grant type being used. The client identifier issued during application registration. Must match the client\_id used in the authorization request. The authorization code received from the authorization server. This is the code obtained in the previous authorization step. The same redirect URI that was used in the authorization request. Must exactly match the previous URI for security validation. The original PKCE code verifier that was used to generate the code\_challenge. Used to prove possession of the authorization code. The client secret for confidential clients. Required if the client is configured with a secret. You will receive an access token that can be used to authenticate API requests. ## Refreshing an Access Token You can use the **refresh\_token** returned in the previous request to continuously retrieve a valid **access\_token** for use with the API. To exchange a **refresh\_token** for an **access\_token**, make the following request: ```bash curl theme={"dark"} curl -X POST 'https://auth.smokeball.com/oauth2/token' \ --header 'Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=refresh_token' \ --data-urlencode 'client_id=xxxxxx' \ --data-urlencode 'refresh_token=xxxxxx' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://auth.smokeball.com/oauth2/token", "headers": { "Authorization": "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=", "Content-Type": "application/x-www-form-urlencoded" }, "body": { "grant_type": "refresh_token", "client_id": "xxxxxx", "refresh_token": "xxxxxx" } } ``` Basic Authentication header containing base64-encoded `client_id:client_secret`. Example: `Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=` Must be `application/x-www-form-urlencoded` for token endpoints. Must be `refresh_token` for this flow. Determines the OAuth 2.0 grant type being used. The client identifier issued during registration. Must match the client ID used in the Basic Auth header. Previously issued refresh token. Used to obtain new access tokens without re-authentication. **Sample Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "access_token":"dmcxd329ujdmkemkd349r", "token_type":"Bearer", "expires_in":3600 } ``` The access token used to authenticate API requests. By default, the returned **access\_token** is valid for 60 minutes. The type of token issued (typically `"Bearer"`). Indicates how the token should be presented in API requests. The lifetime of the access token in **seconds**. After expiration, a new token must be obtained. > See [AWS Cognito User Pool OAuth 2.0 Grants](https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/) for more information ## Migrating from Id Token to Access Token The use of **id\_token** to make API requests has been deprecated. We will be phasing out issuing an **id\_token** when you request a **token** in the future. If you have been using an **id\_token** for your API requests, **no immediate action is required**. You will be contacted with instructions for transitioning to the **access\_token**. You can also [contact us via this link](https://marketplace.smokeball.com/application_forms/api-scope-request-form/partner_applications/new) and provide us with a list of endpoints that you need access to so we can verify that your **access\_token** is appropriately configured with the necessary permissions. > Note: When you transition to **access\_token**, the `scope` query parameter should list only the API scopes your app needs (for example `matters/read`). Please **remember to remove** `openid` from the `scope` parameter in your requests to `https://auth.smokeball.com/oauth2/authorize`, to ensure seamless operation. # API Source: https://docs.smokeball.com/docs/api-docs/98439555e1d41-release-notes We improve the Smokeball API all the time by releasing new features, fixing bugs and updating the documentation. This changelog accounts for all of the changes as they are released, in chronological order. * **Authentication**: Increased the default refresh token expiry from 30 to 180 days, effective 24 August 2026. Applies to refresh tokens issued on or after that date. * **BillingConfiguration**: Added a `PATCH` endpoint to update billing configuration. * **Contacts**: Added `/contacts/batch` endpoint to fetch multiple contacts by ID. * **Tasks**: Limit task notes to adhere to Smokeball app restrictions. * **Events**: Deprecated `eventType` field in favor of new `type` field. * **Files**: Added `matter`, `folder`, and download information to file search results. * **Matters**: Added support for hidden items on the patch endpoint. * **Tasks**: Fixed pagination `href` links on the `/tasks` endpoint to include all applied filters. * **Tasks**: Added `dueDateOnly` and `completedDateOnly` fields; deprecated the previous date/time fields 'dueDate' and 'completedDate'. * **Contacts**: Added `website` field. * **Files**: Fixed intermittennt error when posting files to document sub-folders. * **Matters**: Fixed duplicate empty role items being created when clearing a role via PATCH request. * **MatterTypes**: Fixed webhooks to include missing `items` and `representativeOptions` fields. * **Search**: Added advanced file/document searching functionality for matters. * **Activities**: Fix incorrect zero default tax rate when not supplied. * **BankAccounts**: Added matter balances endpoint. * **Fees**: Added `CreatedDate` field to GET endpoints. * **Folders**: Added `/path` endpoint to retrieve folder hierarchy for a specified folder. * **Matters**: Added missing validation for staff. * **Activities**: Added `InputTaxRate` and `OutputTaxRate` fields to activity codes. * **Layouts**: Fixed errors updating events. * **Staff**: Fixed incorrect paging total size. * **BankAccounts**: Added requisition and fund protection functionality to request payment from trust. * **Fees/Expenses**: Removed PATCH validation check for existing fee/expense records in API endpoints. * **Invoices**: Added GET endpoint to retrieve individual invoices. * **Staff**: Fixed missing `userId` field in webhook payloads. * **Transactions**: Added create functionality for Trust accounts as well as ability to list transactions. * **Expenses**: Fix `outputTax` backwards compatibility issue for AU and UK regions. `outputTax` will now be set if missing from POST payload. * **Portal**: Added client portal API endpoints. * **Tags**: Added contact and matter tags. * **Expenses**: Added `costType` field. * **Users**: Added `bypassMfa` field. * **FirmUsers**: Deprecated the `/firmusers` endpoints and introduced `/firm/users` which behaves in the same way. * **Files**: Added ability to retrieve file history * **Matters**: Added support for multiple attorneys on a matter. * **Contacts**: Added ability to retrieve and update contact tags. * **Matters**: Added ability to retrieve and update matter tags. * **Webhooks**: Resolve matter title inconsistencies in matter webhook payloads. * **Contacts**: Added additional validation to contact relations. * **Events**: `organizer` field has been removed as it is no longer required. * **Files**: File extension renames are now supported using the PATCH endpoint. * **Invoices**: `surcharge` field added to invoices that includes invoice surcharge type and amounts. * **Staff**: `outOfOffice` field added to staff members, this field contains `startDate` and `endDate`. * **Users**: Users can now be registered using Authorization Code Grant using the `users/write` scope. * **Layouts**: Dates are returned in ISO format. * **MatterTypes**: Matter types with multiple categories are now searchable. * **Webhooks**: Uninstalling an app now unsubscribes webhook subscriptions. * **Webhooks**: Added `invoice.created` event type which is triggered when an invoice is created. * **Expenses**: Return UtbmsTaskCode from GET payloads. * **Fees**: Return UtbmsTaskCode from GET payloads. * **Matters**: Fix issue where matter type id could be missing from GET payloads. *No changes* * **Matters**: Add ability to clear referal type using matter PATCH and PUT endpoints. * **Tasks**: Added `isCompleted` query parameter to GET endpoint. * Performance improvements to all API endpoints. * **Files**: Added `isDuplicate` field to GET endpoints. * **Files**: Fixed issue where some files with special characters cannot be downloaded. * **MatterTypes**: Fixed issue where some matter types cannot be found. * **Webhooks**: Added `matter.closed` event type which is triggered when a matter is closed. * **Webhooks**: Fixed issue causing intermittent timeouts. * **FirmUsers**: Fixed intermittent issue where staff was not being set to former when FirmUser mapping is removed. * **ReferralTypes**: Added ReferralTypes API. * **Matters**: Added ability to search for matters using the GET endpoint. * **Matters**: Lead PATCH endpoint bug fixes. * **Invoices**: Added ability to download finalized invoice pdfs. * **Matters**: Matters v2 API is now standard and the Api-Version header is no longer required. * **Invoices**: Added debtors to API payload. * **Webhooks**: Added `invoice.updated` event type. * **Contacts**: UTBMS (US only) and Citizenship details fields added. * **Expenses**: Previously invoiced externally expenses can now be unfinalized. * **Firm**: Added firm status * **MatterTypes**: Categories are now filtered by account white label. * **Events**: Added `MatterId` to GET endpoint. * **Expenses**: Auto-assign callng staff if staff id is not provided. * **Fees**: Auto-assign callng staff if staff id is not provided. * **Firm**: Added AddOns to GET endpoint. * **Matters**: Added missing account validation checks. * **Fees**: Added `CreatedFromActivityId` to GET endpoint. * **Fees**: Resolved issues where StaffId could cause PATCH failures * **Webhooks**: Added `files.updated` event type which is triggered against a matter. Use the cursor returned to rereive the updated files via the files API. * **Files**: Added `IsCancelled` and `IsUploaded` to GET endpoint. * **Matters**: Resolved issues with incorrect matter access permissions. * **Contacts**: Added `correctionsReferenceNumber` and `centrelinkReferenceNumber` to AU endpoints. * **Expenses**: Added PATCH endpoint. * **Fees**: Added PATCH endpoint. * **Files**: Added `isFavorite` field. * **Folders**: Added folder `name` to GET endpoint. * **Webhooks**: Added `matter.converted` event type which is triggered once a lead is converted to a matter. * **Fees**: Added `isInvoiceExternally` to GET. * **Matters**: Added additional validation for `ClientIds` and `OtherSideIds`. * **Matters**: Added `referralType`, `referrerId`, `closedDate`, `leadOpenedDate`, `leadClosedDate`, `leadClosedReason` fields. * **Matters**: Fixed issue where matters could not be created with more than one client. * **MatterTypes**: Added restrictions based on account white label. * **Memos**: Fixed data issue using GET endpoint. * **Tasks**: Add search by `matterId`. * **Expenses**: Fix issue where the `Finalized` field was being ignored on POST and PUT endpoints. * **Files**: Fix issue preventing newly added files from being indexed for search. * **Files**: Fix issue where newly added files are being added to deleted folders. A new folder is now added if the file is being added to a deleted folder. * **Firm**: Added `productId` field to GET endpoint. * **Firm**: Added attorney/solicitor licence numbers to GET endpoint. * **Matters**: Fix issue preventing matter creation when multiple clients are specified. * **Matters**: Several improvements and fixes to the beta PATCH endpoint including: * Added additional validation to multiple fields. * Ensured changes made via the PATCH endpoint reflect in the matter history correctly. * **MatterTypes**: Fix issue exposing deleted matter types from GET endpoint. * **Staff**: Added attorney/solicitor licence numbers to GET endpoint. * **Tasks**: Added missing validation to the `MatterId` and `StaffId` fields. These fields must now be valid GUID values, empty strings are no longer permitted. The `StaffId` is mandatory. * **Expenses**: Added `AssignToFirmOwner` option when creating an expense. * **Files**: Added `DateModified` field when creating a file. * **Files**: Added `DateModified` field when bulk creating files. * **Firm**: Deprecated the `Product` field and introduced the `ProductId` field. * **Firm**: Added support for the Boost Product. * **Matters**: Fix issue with combining search filters, e.g. `IsLead` and `ContactId`. * **Staff**: Added `ColorFill` and `ColorStroke` fields. * **Webhooks**: Improvements to `Matter` webhooks, notifications are more accurate as well as more deterministic. * **Webhooks**: Added `Memo` webhooks. Use the `memo.created`, `memo.updated`, `memo.deleted`, `memo.restored` event types to listen to changes made to memos. * **Archive**: Added Archives API. * **Events**: Added Events API. * **Expenses**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `StaffId`, instead use `Staff`. * `MatterId`, instead use `Matter`. * **Fees**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `StaffId`, instead use `Staff`. * `MatterId`, instead use `Matter`. * **Webhooks**: Added `Task` webhooks. Use the `task.created`, `task.updated`, `task.deleted` event types to listen to changes made to tasks. * **Webhooks**: Added `Event` webhooks. Use the `event.created`, `event.updated`, `task.deleted` event types to listen to changes made to events. * **Webhooks**: Added `LayoutDesign` webhooks. Use the `layoutdesign.updated` event type to listen to changes made to layout designs. * **Webhooks**: Added `Archive` webhooks. Use the `archive.updated` event type to listen to changes made on a matters archive details. * **Activities**: Fixed issues with activity code(s) not being deleted. * **BillingConfiguration**: Added `BillingConfiguration` [GET](/api-reference/matters/get-matter-billing-configuration) and [PUT](/api-reference/matters/update-matter-billing-configuration) endpoints. Billing configurations are set on the matter level. * **Contacts**: Added `Locality` and `County` fields. * **Contacts**: GroupOfPeople are no longer permitted in the UK. * **Files**: Added `DateModified` field to GET responses. * **Firm**: Fixed issues updating `Email` field. * **Firm**: Fixed issues updating `DxAddress` field. * **Matters**: Added `Supervisor` field. * **Matters**: Added ability to filter by multiple `status`. Multiple statuses can now be passed in the query parameters. * **Roles**: Prevent adding roles that are not permitted for the specified matter. * **Staff**: Added `UserId` field. * **TaskDocuments**: Fixed task document(s) not being added to tasks. * **Webhooks**: Added `BillingConfiguration` webhooks. Use the `billingconfiguration.updated` event type to listen to changes to billing configurations on a matter level. * **Activities**: Added additional validation for activity codes. Codes can no longer be more than 20 characters long. * **Contacts**: Added `Name` field to GET response for `GroupOfPeople` contacts. This returns the combined names of the contacts in the group, saving you needing to retrieve the individual contacts. * **Files**: Added [PATCH](/api-reference/files/patch-a-file) endpoint * **Firm**: Added `Owner` field to GET responses. This can be used to determine the whitelabel product linked to the firm. * **MatterTypes**: Added `Type` field to GET responses. This can be used to determine if the type is for a Lead or Matter. * **Tasks**: Added `Duration` field. This follows the `ISO 8601` duration format (e.g. `PT4H33M`) * **Tasks**: Fixed categories not saving when adding or updating tasks. * **Webhooks**: Added `error` event type. This can be used to listen to errors when your API requests fail, see [Error Handling](/docs/api-docs/wivbkstcwngb5-webhooks#error-handling). * **Contacts**: Added ability to exclude deleted contacts from GET response (see `ExcludeDeletedContacts` [query parameter](/api-reference/contacts/get-contacts)). * **Contacts**: Added `BirthFirstName`, `BirthMiddleName`, `BirthLastName` fields. * **Contacts**: Added `PreviousNames` field. * **Contacts**: Added `LanguageOfInterpreter` field. * **Contacts**: Added free text option for `Gender` field. * **Contacts**: Fixed validation issues when creating new contacts. * **Matters**: Added ability to filter matters by contact id (see `ContactId` [query parameter](/api-reference/matters/get-matters)). * **Matters**: Matter access can now be restricted by `UserId` when using Client Credentials grant (see [Acting on behalf of a user](/docs/api-docs/ivmfjbc61tvjs-making-requests#acting-on-behalf-of-a-user)). * **Leads**: Restricted leads API to Grow and Prosper tiered clients. * **Files**: File access can now be restricted by `UserId` when using Client Credentials grant (see [Acting on behalf of a user](/docs/api-docs/ivmfjbc61tvjs-making-requests#acting-on-behalf-of-a-user)). * **Activities**: Fix `TaxInclusive` field returning incorrect value. * **Files**: Added ability to add additional data on creation of a file (see [Files](/api-reference/files/add-file-to-a-matter)). * **Roles**: Fix some [DELETE](/api-reference/roles/remove-role-from-a-matter) endpoint issues. * **Relationships**: Added [DELETE](/api-reference/relationships/remove-relationship-from-a-role-in-a-matter) relationship endpoint. * **Tasks**: Added ability to link files and memos to tasks (see [TaskDocuments](/api-reference/tasks/get-task-documents)). * **Webhooks**: Added `MatterItems` webhooks. * **Webhooks**: Fixed `MatterTypeId` field missing in some `matter.updated` event payloads. * **Contacts**: Fix webhook events not triggering for deleted contacts from Smokeball native application. * **Matters**: Added `Branch` field. * **Matters**: Added `OtherSideIds` and `OtherSideRole` to POST request so matters and leads can be created with clients as well as othersides. * **Matters**: Fix to paging url losing status on consecutive pages. * **Memos**: Added `UserId` to POST and PUT requests to create or update a memo on behalf of a user. * **SubTasks**: Added SubTasks API. * **Stages & StageSets**: Added Stages and StagesSets API. * **Contacts**: Added ability to restore contacts in PUT endpoint. * **Contacts**: Added ability to relate a person to a group of people. * **Contacts**: Added ability to add and update trusts. * **Fees**: Removed check for existing `Fee` when performing PUT, requests are accepted and queued asynchonously like a POST. * **Expenses**: Removed check for existing `Expense` when performing PUT, requests are accepted and queued asynchonously like a POST. * **Webhooks**: Added `MatterStages` webhooks. * **Webhooks**: Fixed `RequestId` not propogating from some Matter API calls. * **Webhooks**: Fixed null fields on `Firm` update webhook events. * **Firm**: Added `Logo` field to GET responses. * **Contacts**: Added `MaritalStatus` field. * **Contacts**: Added `SpecialNeeds` field. * **Tasks**: Added `CompletedDate` field. * **Tasks**: Added `LastUpdated` field to GET responses. * **Firm**: Added validation when creating a firm to ensure the user is not associated to another firm. * **FIrm**: Added Product when creating a firm with 3 product types `(BillingOnly, SmokeballMid and Smokeball)`. * **Firm**: Added `IsInternal` field when creating a firm so that a firm can be flagged as an internal firm and skip payment subscription. * **Firm**: Added `ABN`, `ACN` and postal details, `CareOf`, `PoBoxType` and `PoBoxNumber` to firm MailingAddress. * **Users**: Added functionality to create users with no password, and email is sent out with a temporary password. * **Matters**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `MatterTypeId`, instead use `MatterType`. * `ClientIds`, instead use `Clients`. * `OtherSideIds`, instead use `OtherSides`. * `PersonResponsibleStaffId`, instead use `PersonResponsible`. * `PersonAssistingStaffId`, instead use `PersonAssisting`. * `OriginatingStaffId`, instead use `OriginatingStaff`. * **Matters**: Added OriginatingStaff to matter. * **Contacts**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `StaffIds`, instead use `Staff`. * `PersonIds`, instead use `People`. * **Roles**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `ContactId`, instead use `Contact`. * `RepresentativeIds`, instead use `Representatives`. * `RelationshipIds`, instead use `Relationships`. * **Roles**: Added DELETE role endpoint for removing roles from a matter. * **Roles**: Added `IsOtherSide` field to determine if the role is the OtherSide of a matter. * **Roles**: Fixed issues with Layout subitems not being created. * **Relationships**: Deprecated the following fields in the response payloads, this includes the fields in the Webhooks payload: * `ContactId`, instead use `Contact`. * `RepresentativeIds`, instead use `Representatives`. * **Files**: Added `UserId` to files and folders. * **Webhooks**: Add `LayoutMatterItem` webhooks. # Logout Endpoint Source: https://docs.smokeball.com/docs/api-docs/9de98u0xqsckx-logout-endpoint Use the logout endpoint to securely end user sessions The `/logout` endpoint is a redirection endpoint. It signs the user out and redirects either to an authorized sign-out URL for your app client, or to the /login endpoint. The available parameters in a GET request to the `/logout` endpoint are tailored to Amazon Cognito hosted UI use cases. > See [AWS Cognito Logout Endpoint Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html) for more information ## Logout Request Make a GET request to `https://auth.smokeball.com/oauth2/logout` with the following query parameters: ```bash curl theme={"dark"} curl --request GET 'https://auth.smokeball.com/oauth2/logout?response_type=code&client_id=xxxxx&redirect_uri=https://xxxxxxxx' ``` ```json http theme={"dark"} { "method": "GET", "url": "https://auth.smokeball.com/oauth2/logout", "query": { "response_type": "code", "client_id": "xxxxx", "redirect_uri": "https://xxxxxxxx" } } ``` Optional: Bearer token for authenticated logout requests. Example: `"Bearer dmcxd329ujdmkemkd349r"` Must be set to `"code"` for OAuth2 logout redirection. Ensures the authorization server returns an authorization code. The client identifier issued during application registration. Used to verify the application initiating logout. Example: `"xxxxx"` The URI where the user will be redirected after logout. Must match a pre-registered logout redirect URI. Example: `"https://xxxxxxxx"` ## Request Headers * **Authorization**: Bearer token ## Response The logout endpoint responds with a status code indicating the success (200) or failure of the logout operation. # Overview Source: https://docs.smokeball.com/docs/api-docs/c916c683c136e-authentication-overview Overview of the authentication process for the Smokeball API Your OAuth 2.0 `client_id` and `client_secret` credentials can be found in the App Details section of the Developer Console. There are two OAuth 2.0 flows used to authenticate with the Smokeball API based on your integration scenario: 1. [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) - This is used to connect to the Smokeball API as an authenticated Smokeball user. The Smokeball user will authenticate using their credentials and an authorization code is passed back to your supplied redirect uri(s) which you can then exchange for an access token. 2. [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) - This flow is used when connecting to the Smokeball API for server-to-server requests outside the context of a Smokeball user. Additionally, we provide a [Logout Endpoint](/docs/api-docs/9de98u0xqsckx-logout-endpoint) for securely ending user sessions. Access to API resources is controlled by [Scopes](/docs/api-docs/qf83nz2scopes-scopes), which limit what your app can read and write. # Errors Source: https://docs.smokeball.com/docs/api-docs/e4f5a6b7c8d9e-errors Understanding API error responses All error responses from the Smokeball API follow [Microsoft's ProblemDetails standard](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails) (RFC 7807), with one extension described below. ## ProblemDetails A URI reference that identifies the problem type. When dereferenced, it provides human-readable documentation for the problem. Example: `https://tools.ietf.org/html/rfc7231#section-6.5.1` A short, human-readable summary of the problem type. Does not change between occurrences of the same problem. Example: `"One or more validation errors occurred."` The HTTP status code for this occurrence. Example: `400` A human-readable explanation specific to this occurrence of the problem. Example: `"See the errors field for details."` A URI reference that identifies the specific occurrence. An array of error objects describing individual validation or field-level errors. See [the errors field](#errors) below. ## Example Response ```json theme={"dark"} HTTP/1.1 400 Bad Request Content-Type: application/problem+json { "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "detail": "See the errors field for details.", "instance": "/contacts", "errors": [ { ... } ] } ``` ## Common Error Types Here are some common HTTP error responses you may encounter when working with the Smokeball API: ### 400 Bad Request This means there is something wrong with the request you sent. Common examples: * A required field is missing * A field value is in the wrong format * You sent a value the API does not accept ### 403 Forbidden This means the request was valid, but you are not allowed to do it. Common examples: * Your API key, client ID, or client secret is wrong * Your credentials have expired or were revoked * Your app does not have the scope needed for this endpoint * The signed-in user does not have access to that record or action ### 404 Not Found This means the API could not find what you asked for. Common examples: * The URL or endpoint is wrong * The record ID does not exist * You are calling the right endpoint in the wrong environment ## Extensions ### errors The `errors` field is a Smokeball extension to the ProblemDetails standard. The ProblemDetails standard leaves the shape of `errors` loosely defined. Smokeball returns `errors` as an **array of objects** instead. There are two reasons for this: 1. **Multiple errors per field**: a dictionary approach typically collapses all messages for a field into a string array, losing any structured metadata (such as error codes) attached to each individual error. 2. **Preserved ordering**: an array guarantees that errors are returned in a stable, meaningful order (e.g. the order fields appear in the request body), whereas dictionary key ordering is not guaranteed across all languages and runtimes. # Client Credentials Grant Source: https://docs.smokeball.com/docs/api-docs/h72024p76i8e1-client-credentials-grant Use the Client Credentials Grant to authenticate and authorize server-to-server operations The **Client Credentials** grant type is typically used for server-to-server API operations. In this scenario, you are not acting as a Smokeball user but a type of "service" that can perform operations on an Account level. ## Request an Access Token Make a POST request to `https://auth.smokeball.com/oauth2/token` with the following parameters and headers: ```bash curl theme={"dark"} curl --request POST 'https://auth.smokeball.com/oauth2/token' \ --header 'Authorization: Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'client_id=xxxx' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://auth.smokeball.com/oauth2/token", "headers": { "Authorization": "Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=", "Content-Type": "application/x-www-form-urlencoded" }, "body": { "grant_type": "client_credentials", "client_id": "xxxx" } } ``` Basic Authentication header containing the `client_id` and `client_secret` (base64-encoded). Format: `Basic base64(client_id:client_secret)`. Example: `Basic Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ=` Must be set to `application/x-www-form-urlencoded`. Ensures the request body is properly encoded. Must be set to `client_credentials` for this OAuth 2.0 flow. Indicates the request is for client credentials grant. The client identifier that has been issued. Used to authenticate the client making the request. Do not include a `scope` parameter. The token is issued with every [scope](/docs/api-docs/qf83nz2scopes-scopes) assigned to your app in the Developer Console. > The **Authorization** header must be the string "Basic" followed by your client\_id and client\_secret with a colon : in between, Base64 Encoded. For example, client\_id:client\_secret is Y2xpZW50X2lkOmNsaWVudF9zZWNyZXQ= **Sample Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "access_token": "dmcxd329ujdmkemkd349r...", "expires_in": 3600, "token_type": "Bearer" } ``` The token to use in the `Authorization` header when making API requests. The lifetime of the access token in **seconds**. After expiration, a new token must be obtained. The type of token returned. Always `Bearer`. ## Caching Access Tokens We recommend caching your access token and reusing it until it expires, rather than requesting a new token for every API request. Requesting a token on every request adds latency to each call and places unnecessary load on the token endpoint. When you receive a token, store it along with its expiry time (calculated from `expires_in`, which is the token lifetime in seconds). Reuse the cached token for all API requests until it is close to expiry, then request a new one. A common approach is to refresh the token slightly before it expires (for example, 60 seconds early) to avoid using an expired token on an in-flight request. > See [Making Requests - Server-to-Server requests](/docs/api-docs/ivmfjbc61tvjs-making-requests#server-to-server-requests) for details on using Client Credentials. # Making Requests Source: https://docs.smokeball.com/docs/api-docs/ivmfjbc61tvjs-making-requests Make requests to the API All requests to the Smokeball API require a valid API key issued by Smokeball and an access token (see [Authentication](/docs/api-docs/c916c683c136e-authentication-overview)). Make sure to [include the appropriate scopes](/docs/platform-docs/867hiv3k5bt1u-build-app#scopes) in your app settings before attempting any API requests. ## Example Request ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/contacts/" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer eyJraWQiOiJseVpBaHdjSW5UdVl6U" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/contacts/", "headers": { "x-api-key": "Y2xpZW50X2lkOmNsaWV", "Authorization": "Bearer eyJraWQiOiJseVpBaHdjSW5UdVl6U..." } } ``` Client authentication key issued by Smokeball. Used to identify the application making the request. Example: `Y2xpZW50X2lkOmNsaWV` Bearer token for user authentication. Must be prefixed with `Bearer`. Example: `Bearer eyJraWQiOiJseVpBaHdjSW5UdVl6U...` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/contacts", "offset": 0, "limit": 500, "size": 761, "first": { "href": "https://api.smokeball.com/contacts" }, "next": { "href": "https://api.smokeball.com/contacts?limit=500&offset=500", "rel": "collection" }, "last": { "href": "https://api.smokeball.com/contacts?limit=500&offset=500", "rel": "collection" }, "value": [ { "href": "https://api.smokeball.com/contacts/323f9755-045a-4b68-a536-eb566b8428ff", "id": "323f9755-045a-4b68-a536-eb566b8428ff", "groupOfPeople": { "people": [ { "href": "https://api.smokeball.com/contacts/a1d868da-1243-4b9f-a8e3-cfc5c4abda50" }, { "href": "https://api.smokeball.com/contacts/ea4ca7b9-b826-4840-a8b5-94e0c6977c65" } ], "residentialAddress": { "addressLine1": "1247 N Clark Street", "city": "Chicago", "state": "IL", "zipCode": "60607", "county": "Cook", "country": "United States" } } } ] } ``` ## Paged Responses You will notice that some requests will have the optional parameters `limit` and `offset`. These are used to control how many results are returned in a request that returns a list of results. In the response above, you can see the details of the paging properties in the first section of the response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/contacts", "offset": 0, "limit": 500, "size": 761, "first": { "href": "https://api.smokeball.com/contacts" }, "next": { "href": "https://api.smokeball.com/contacts?limit=500&offset=500", "rel": "collection" }, "last": { "href": "https://api.smokeball.com/contacts?limit=500&offset=500", "rel": "collection" } } ``` **Paging Properties** | Property | Description | | - | - | | `offset` | The number at which the results set begins | | `limit` | The number of results in the response | | `size` | The total number of results | | `first` | A link to the first result set | | `next` | A link to the next result set | | `last` | A link to the last result set | > By default, the `limit` will be set to a maximum of 500 results. ## Server-to-Server requests The details below apply **only** if your Client Credentials Grant allows access to multiple accounts It does not apply for [Private Apps](/docs/platform-docs/4f56d789e123f-creating-app#app-type) created via the Developer Console, which are restricted to a single account. If your app was created from the Developer Console, you can ignore this section. When using the [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) to perform server-to-server requests, and your credentials are authorized to access more than one account, you must explicitly specify which account you are targeting by prefixing the URL with the `accountId` in this format: > [https://api.smokeball.com/\{accountId}/\{resource-path}](https://api.smokeball.com/\{accountId}/\{resource-path}) For example, using the contacts resource, a typical URL would be:\ [https://api.smokeball.com/contacts/](https://api.smokeball.com/contacts/)\ But for a server-to-server request specifying the `accountId: ea4ca7b9-b826-4840-a8k5-94e6c6937c65`, the URL becomes: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts/" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts/", "headers": { "x-api-key": "Y2xpZW50X2lkOmNsaWV", "Authorization": "Bearer Y2xpZW50X2lkOmNsaWV" } } ``` **Response** > Note: All links in the response will include the accountId prefix: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts", "offset": 0, "limit": 500, "size": 761, "first": { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts" }, "next": { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts?limit=500&offset=500", "rel": "collection" }, "last": { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts?limit=500&offset=500", "rel": "collection" }, "value": [ { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts/323f9755-045a-4b68-a536-eb566b8428ff", "id": "323f9755-045a-4b68-a536-eb566b8428ff", "groupOfPeople": { "people": [ { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts/a1d868da-1243-4b9f-a8e3-cfc5c4abda50" }, { "href": "https://api.smokeball.com/ea4ca7b9-b826-4840-a8k5-94e6c6937c65/contacts/ea4ca7b9-b826-4840-a8b5-94e0c6977c65" } ], "residentialAddress": { "addressLine1": "1247 N Clark Street", "city": "Chicago", "state": "IL", "zipCode": "60607", "county": "Cook", "country": "United States" } } } ] } ``` ### Acting on behalf of a user Because server-to-server requests are not performed using a user token there is no `userId` associated with each request. A `userId` is necessary for permission checking or ensuring that the UI reflects the change done by the specified user. To act on behalf of a certain user simply supply the `UserId` header with your requests. The value must be that of a [User](/api-reference/firm/get-firm-user-mappings) in the firm. ## Tracking your requests Since all POST/PUT requests are handled asynchronously by the Smokeball API, it is useful to be able to track the changes especially when using [Webhooks](/docs/api-docs/wivbkstcwngb5-webhooks). This is acheived by supplying the `RequestId` header with your requests. If the `RequestId` header is supplied, it will be returned as a response header for every request you make. It will also be included in all webhook callbacks so you can track what data was impacted by your request. # Leads Source: https://docs.smokeball.com/docs/api-docs/plu740klph0ho-leads Create and update leads ## Lead Types Smokeball comes with a range of pre-configured lead matter types for different areas of law. These are provided by Smokeball's internal content team and are not user editable. A lead matter type is used to assign the lead being created to a relevant area of law and state / location. > The matter types used when creating a 'Lead' are different from the one's used to create a 'Matter'. Lead Matter Types When creating a lead in Smokeball we can see a list of lead Matter Types. Here, *Company Formation* is a lead matter type, under the *Business* category. ### Getting a Lead Matter Type ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/mattertypes?location=IL&type=1" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/mattertypes", "query": { "Location": "IL", "Type": 1 // Use this parameter to get lead specific matter types - where 1=LeadType } } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/mattertypes", "offset": 0, "limit": 500, "size": 18, "first": { "href": "https://api.smokeball.com/mattertypes" }, "value": [ { "href": "https://api.smokeball.com/mattertypes/8aca3574-aecb-4a6f-991d-680861bece1d_il", "id": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "versionId": "32e86c38-534d-4362-95a1-f5c287a8d4b7", "name": "Financing", "category": "Real Estate", "representativeOptions": [ "Borrower", "Lender", "MortgageBroker", "Seller", "CooperativeApartment" ], "location": "IL", "deleted": false }, { "href": "https://api.smokeball.com/mattertypes/2d5aca9b-9469-44d4-8616-766a864432aa", "id": "2d5aca9b-9469-44d4-8616-766a864432aa", "versionId": "1410cfea-723e-4514-8901-c92dab1bd5f2", "name": "Buy", "category": "Real Estate", "representativeOptions": [ "Buyer" ], "location": "IL", "deleted": false } ] } ``` > Here the important information to note is the `id` and `representativeOptions` of the lead matter type. We'll need those when creating a lead. *** ## Contacts In order to successfully create a lead, we're going to need to provide a contact that is the potential client. In Smokeball's desktop app you can create a contact as a Person, Firm/Business/Organization or Trust. Currently, the API supports the following contact types: * Person * Company New Contact ### Creating a Contact To create a contact you can make the following request which would create a person called 'Test Contact'. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/contacts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{"person":{"firstName":"Test","lastName":"Contact","email":"test@gmail.com"}}' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/contacts", "headers": { "Content-Type": "application/json" }, "body": "{\n \"person\": {\n \"firstName\": \"Test\",\n \"lastName\": \"Contact\", \n \"email\": \"test@gmail.com\",\n }\n}\n\n" } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "href": "https://api.smokeball.com/contacts/9a9ce552-6bee-45e4-8eb1-afe2c18c489f" } ``` Take note of the Contact's `id` returned in the response, we'll need that when creating a lead. Alternatively, if the contact already exists in Smokeball, you can query it to find it's id. > *Tip* > > When creating a contact you can pass in an `externalSystemId` if you want to store your own reference id with this contact. *** ## Staff In Smokeball, staff can be assigned to a lead as the **Attorney Responsible** or the **Person Assisting**. The lead list can then be filtered by Attorney responsible, etc. Staff If you want to specify the Attorney Responsible or Person Assisting when creating a lead via the API, then we will first need to retrieve the staff member's id. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/staff" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/staff" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/staff", "size": 28, "first": { "href": "https://api.smokeball.com/staff" }, "value": [ { "href": "https://api.smokeball.com/staff/66f7fa24-bb03-4d89-ac28-e19620cc1e45", "id": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "versionId": "cfdbb798-1f2e-4355-87bc-620cbc62acc2", "firstName": "Test", "lastName": "Staff", "phone": { "number": "200518540309" }, "cell": {}, "email": "teststaff@gmail.com", "avatar": "https://staging.smokeball.com/FirmManagement/Firm/RedirectToStaffProfileImageUrl?accountId=2f3143de-2bfe-4742-9e9c-7c16b48bdef7&staffId=66f7fa24-bb03-4d89-ac28-e19620cc1e45", "former": false, "enabled": true }, { "href": "https://api.smokeball.com/staff/c85d28cb-a760-4627-aa59-0a853c2e65ed", "id": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "versionId": "23dbdc50-6171-4408-8150-413cb293a249", "title": "", "firstName": "Hunter", "lastName": "Steele", "email": "hunter@gmail.com", "avatar": "https://staging.smokeball.com/FirmManagement/Firm/RedirectToStaffProfileImageUrl?accountId=2f3143de-2bfe-4742-9e9c-7c16b48bdef7&staffId=c85d28cb-a760-4627-aa59-0a853c2e65ed", "former": false, "enabled": false } ] } ``` *** ## Creating a lead Now we have everything we need to create a lead. ```bash theme={"dark"} curl --request POST "https://api.smokeball.com/matters" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{"matterTypeId":"8aca3574-aecb-4a6f-991d-680861bece1d_IL","clientIds":["9a9ce552-6bee-45e4-8eb1-afe2c18c489f"],"clientRole":"Borrower","description":"Created via API","status":"Open","dateOpened":"2020-05-20T04:20:55.035Z","personResponsibleStaffId":"c85d28cb-a760-4627-aa59-0a853c2e65ed","personAssistingStaffId":"66f7fa24-bb03-4d89-ac28-e19620cc1e45","isLead":true}' ``` ```json theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/matters", "headers": { "Content-Type": "application/json" }, "body": { "matterTypeId": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "clientIds": [ "9a9ce552-6bee-45e4-8eb1-afe2c18c489f" ], "clientRole": "Borrower", "description": "Created via API", "status": "Open", "dateOpened": "2020-05-20T04:20:55.035Z", "personResponsibleStaffId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "personAssistingStaffId": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "isLead": true } } ``` Bearer token for user authentication. Format: `Bearer YOUR_ACCESS_TOKEN` Must be `application/json` for this endpoint. The lead type identifier. Example: `8aca3574-aecb-4a6f-991d-680861bece1d_IL` (the id we found earlier). Contact Ids associated with the lead. Minimum: 1 client required. Example: `["9a9ce552-6bee-45e4-8eb1-afe2c18c489f"]` The client's role in the lead (e.g., "Borrower", "Seller"). If left blank, the default role associated to the lead type will be used. Example: `"Borrower"` Description of the lead. Example: `"Created via API"` Initial lead status (typically "Open"). Can be `Open`, `Pending`, `Closed`, `Deleted` or `Cancelled` Example: `"Open"` Lead creation date in ISO 8601 format (this can be backdated). Example: `"2020-05-20T04:20:55.035Z"` Staff Id of the primary responsible staff member (optional). Example: `"c85d28cb-a760-4627-aa59-0a853c2e65ed"` Staff Id of the assisting staff member (optional). Example: `"66f7fa24-bb03-4d89-ac28-e19620cc1e45"` Whether the matter is marked as a lead (default: false). Example: `true` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "51cefb72-6247-4ca2-8926-5d14d65f7cb9", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" } ``` # Scopes Source: https://docs.smokeball.com/docs/api-docs/qf83nz2scopes-scopes OAuth 2.0 scopes used to limit access to the Smokeball API Scopes are used to limit access to your Smokeball App. Each API resource has a pair of scopes: * **Write scopes** permit updates (e.g. POST, PUT, PATCH requests) * **Read scopes** permit reads (e.g. GET requests) Multiple scopes may be needed in order to perform actions on child resources. For example, `matters/write` and `documents/write` are both needed for adding files to a matter. ## Requesting scopes Scopes are requested via the `scope` parameter (space-delimited) when authorizing with the [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant). Always include the `scope` parameter and list every scope your app needs, for example `scope=contacts%2Fread%20matters%2Fread%20matters%2Fwrite`. Each requested scope must be assigned to your app in the Developer Console. The install URL shown on your app's page in the Developer Console is generated for you. It includes every scope currently assigned to the app, sorted alphabetically, and changes as you add or remove scopes. Copy it from the console rather than building it by hand. If the `scope` parameter is omitted, the authorization server currently falls back to issuing a token with all scopes assigned to the app. OAuth 2.0 expects clients to request their scopes explicitly, so do not rely on this fallback. The [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) does not take a `scope` parameter. Tokens issued by that grant carry every scope assigned to the app in the Developer Console. You may grant yourself access to any scope in **Staging**, but scopes must be approved for usage in **Production** when your app is [submitted for approval](/docs/platform-docs/5g67h890f234g-publishing-app). ## Scope reference ### Matters | Scope | Description | | - | - | | `matters/read` | Read matters | | `matters/write` | Write matters | | `mattertypes/read` | Read matter types | | `items/read` | Read matter items | | `layouts/read` | Read layout matter items | | `layouts/write` | Write layout matter items | | `layoutdesigns/read` | Read layout designs | | `layoutdesigns/write` | Write layout designs | | `roles/read` | Read matter roles | | `roles/write` | Write matter roles | | `stages/read` | Read matter stages across the entire firm | | `stages/write` | Write matter stages across the entire firm | | `stagesets/read` | Read matter stage sets | | `stagesets/write` | Write matter stage sets | | `memos/read` | Read matter memos | | `memos/write` | Write matter memos | | `documents/read` | Read matter files | | `documents/write` | Write matter files | | `archive/read` | Read matter archives | | `archive/write` | Write matter archives | | `referraltypes/read` | Read referral types | | `referraltypes/write` | Write referral types | ### Billing & Accounting | Scope | Description | | - | - | | `bankaccounts/read` | Read your bank accounts and transactions | | `bankaccounts/write` | Write your bank accounts and transactions | | `activitycodes/read` | Read activity codes | | `activitycodes/write` | Write activity codes | | `billingconfiguration/read` | Read matter billing configurations | | `billingconfiguration/write` | Write matter billing configurations | | `expenses/read` | Read matter expenses | | `expenses/write` | Write matter expenses | | `fees/read` | Read matter fees | | `fees/write` | Write matter fees | | `invoices/read` | Read matter invoices | | `invoices/write` | Write matter invoices | ### Contacts, Firm & Users | Scope | Description | | - | - | | `contacts/read` | Read contacts | | `contacts/write` | Write contacts | | `firm/read` | Read firm details | | `firm/write` | Write firm details | | `firmusers/read` | Read firm users | | `firmusers/write` | Write firm users | | `staff/read` | Read staff | | `staff/write` | Write staff | | `users/read` | Read users | | `groups/read` | Read authorization groups | | `groups/write` | Write authorization groups | | `policies/read` | Read authorization policies | | `policies/write` | Write authorization policies | ### Communication & Events | Scope | Description | | - | - | | `events/read` | Read events | | `events/write` | Write events | | `notifications/read` | Read notifications | | `notifications/write` | Write notifications | ### Tasks | Scope | Description | | - | - | | `tasks/read` | Read tasks | | `tasks/write` | Write tasks | ### Platform & Integration [Plugins](/docs/api-docs/scmo7vmtln5hm-plugins) are not yet available for partners. They will be supported in a future developer console version, so the `plugins/read` and `plugins/write` scopes cannot be used yet. | Scope | Description | | - | - | | `webhooks/read` | Read webhook subscriptions | | `webhooks/write` | Write webhook subscriptions | | `plugins/read` | Read plugins | | `plugins/write` | Write plugins | | `search/write` | Search your files | | `odata/read` | Read reporting data (fees, expenses, activity codes, bank accounts, contacts, matters, payments, roles, staff, transactions, invoices) | # Plugins Source: https://docs.smokeball.com/docs/api-docs/scmo7vmtln5hm-plugins Create and update plugins Plugins are not yet available for partners. They will be supported in a future developer console version. Smokeball offers a simple Plugin framework for developers to create custom web views within Smokeball desktop. A Plugin is defined by an integration partner, and can be assigned or subscribed to by multiple Smokeball firms. Smokeball offers two implementations of a Plugin - Button or Tab. Partners can customize the appearance of the button/tab for what is shown to users. Plugins are managed through two resources: [Plugins](/api-reference/plugins/create-a-new-plugin) for creating or managing Plugins [PluginSubscriptions](/api-reference/plugins/subscribe-account-to-plugin) for assigning a Plugin to one or many Smokeball firms. ## Placement Keys One of the most important fields of a plugin is its `placement` key, which defines where the plugin appears in Smokeball. The syntax of a placement key for the Plugins API is `page-zone`. Pages and Zones are described in further detail below: ### Desktop Pages Smokeball desktop supports two pages where plugins can be placed: 1. Main Window, denoted by `Window` key 2. Matter Window, denoted by `MatterWindow` key ### Desktop Zones The zones are unique places on a page where content can appear. For instance, a page can have both button zones and tab zones and the placement key determines where the Plugin appears in the page. Smokeball determines how a set of plugins appear in the zone, generally at the end of the existing list. For ribbon buttons, they will be placed into their own ribbon group at the end of the zone. Below are the supported Matter window zones: `MatterWindow-MatterDetailsRibbon` - Places a Tab in Matter page `MatterWindow-MatterDetailsRibbonTab` - Places a Button in the 'Matter' Tab of Matter page Below are the supported Main window zones: `Window-SmokeballRibbonTab` - Places a Button in the 'Smokeball' Tab of Main page `Window-MainTabs` - Places a navigation tab inside the Smokeball left-side tabs `Window-MainRibbon` - Places a tab in the main window ribbon ## SDK Support All browsers opened by Plugins support integration with the Smokeball SDK as long as the internal browser is used, when `attributes.page.useDefaultBrowser` is `true`. See [SDK Documentation](/docs/sdk-docs/675c9816cdaf6-getting-started) for usage information. ## Creating a Plugin Currently, Plugins can only be created and maintained by select partners. Smokeball is currently controlling the process of which accounts can access plugins. ## Subscribing Smokeball Firms to Plugins Smokeball firms are subscribed to plugins through the [PluginSubscriptions](/api-reference/plugins/subscribe-account-to-plugin) resource. Partners authorized through the Client Credentials Grant can then subscribe accounts to their plugins. Available plugins are scoped to API client ids, meaning that you can only subscribe to plugins that were created using the same API client id. Smokeball firms that have subscribed to plugins will always receive the latest version of your plugins, and would be automatically updated to the latest changes. ## Loading your Plugin When a Plugin is accessed in Smokeball, a request is dynamically made to the endpoint defined in the Plugins `requestEndpointUrl` field. The url returned by this request is then loaded into Smokeball. For more detailed usage documentation, please refer to the information below. ### Requesting your URL Plugins support two fields which help load your view into Smokeball. | Field | Description | | - | - | | `requestEndpointUrl` | Used to request a URL from your backend. | | `key` | Used to calculate a signature which must be used by your backend to verify that the request is from Smokeball. | When a Plugin view is loaded, a `POST` request is made to the supplied `requestEndpointUrl` url with a payload similar to below: ```json theme={"dark"} { "method": "POST", "path": "/", "query": {}, "url": "https://your.app", "headers": { "Signature": "3cafe40f92be6ac77d2792b4b267c2da11e3f3087b93bb19c6c5133786984b44", "RequestId": "e56f2c3f-b6de-4310-a7a2-c139d62f9711", "Timestamp": "638609288928990639", "Content-Type": "application/json; charset=utf-8" }, "body": { "accountId": "a58f2f17-f138-4432-9965-d2cb539cde4d", "userId": "b3d70217-791f-4d82-9fb8-2fcff5c04e58", "userEmail": "user@organization.com", "matterId": "86375bb7-0720-4dc6-b584-458bd7865d9e", "embedToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" } } ``` The body of the request contains the following fields: | Field | Description | | - | - | | `accountId` | The unique identifier of the Smokeball firm (account) that the user belongs to. | | `userId` | The unique identifier of the user who triggered the plugin. | | `userEmail` | The email address of the user who triggered the plugin. | | `matterId` | Optional unique identifier of the matter currently open. Only present when the plugin is opened within the context of a matter. | | `embedToken` | Optional JWT token that can be exchanged for a [Smokeball access token via the SDK](/docs/sdk-docs/b4c6e12d3f9a0-authentication). Only present if the plugin is associated to an app ClientId. | Additional fields may also be added to this payload, depending on the source of the plugin. Your endpoint should use this information to generate a URL to be viewed by the user. This url should be returned in the JSON response like so: ```json theme={"dark"} { "url": "https://your-site.com/app" } ``` > It is critical that the above structure is used in the response payload. If an incorrect format is used or the URL is missing, the user will not be able to use your app. ### Request Validation It is important that you verify that the URL request is from Smokeball to prevent malicious usage of your endpoint. If you supplied a `Key` property when creating a Plugin, a header named `Signature` will be included when Smokeball calls your `requestEndpointUrl`. The signature is a HMAC SHA256 hash that was created with the key that you provided. You can perform the same hashing process on your processing side to verify that the request came from Smokeball. ### Computing the HMAC The `Signature` hash is calculated using these three bits of information: 1. The `Timestamp` header which is the ticks representation of the UTC datetime (.Net format) the request was sent 2. The `RequestId` header 3. Your `ClientId` that you use to authenticate with the API These three parameters need to be concatenated into a single string, seperated by a `|` and can then be used to create the hash. ```csharp c# theme={"dark"} // Key you provided in the Plugin creation var key = "ei7641529ue420n8b9aa"; var timestamp = "638609288928990639"; var requestId = "e56f2c3f-b6de-4310-a7a2-c139d62f9711"; var clientId = "lou1qnn0llav95f"; var payload = $"{timestamp}|{requestId}|{clientId}"; var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); return BitConverter.ToString(hash).Replace("-", "").ToLower(); ``` ```javascript javascript theme={"dark"} const crypto = require('crypto'); const key = "ei7641529ue420n8b9aa"; const timestamp = "638609288928990639"; const requestId = "e56f2c3f-b6de-4310-a7a2-c139d62f9711"; const clientId = "lou1qnn0llav95f"; const payload = `${timestamp}|${requestId}|${clientId}`; const hmac = crypto.createHmac('sha256', key); hmac.update(payload); const hash = hmac.digest('hex'); return hash; ``` This produces the hash string `58817681863148b0e624c00f3094f99e1af31cd7b99a3c2e0655d64a2764d650` ### Guarding against replay attacks A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks Smokeball provides a `Timestamp` header which is the ticks representation (.NET format) of when the request was sent. It is also part of the verification signature so an attacker can not change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you may choose to reject the request. ## Limitations ### API Not all Plugin API fields are currently in use. For now, please assume these fields are not in use by Smokeball and may be supported in future: * `endpoint` field has been deprecated. Use `requestEndpointUrl` for all new plugins. You can migrate an existing app to the new field by updating the `endpoint` field to null, and set `requestEndpointUrl` to your new url. * `availability` - availability settings are currently not supported. ### Web App Plugins are currently not supported in the Smokeball web app. # Rate Limits Source: https://docs.smokeball.com/docs/api-docs/vj4jovpoopdov-rate-limits Understanding Smokeball API rate limits and throttling Throttling and limiting is a policy that affects the frequency an API can be called. They are put in place to protect server infrastructure from abuse or misuse. Smokeball employs rate limits to enable consistent load allocation across our platform. ## How Rate Limiting Works The Smokeball API uses the [token bucket algorithm](https://en.wikipedia.org/wiki/Token_bucket). Smokeball API partners are assigned a "Burst" and "Rate" Limit. * The **burst** limit defines the number of requests the API can handle concurrently * The **rate** limit defines the number of allowed requests per second ## Default Limits Unless otherwise specified, Smokeball API partners are assigned the following limits: | Region | Rate (requests per second) | Burst (concurrent requests) | | :- | :-: | :-: | | US | 5 | 5 | | AU | 5 | 5 | | UK | 5 | 5 | ## Handling Rate Limits When you exceed the rate limit, the API will return a `429 Too Many Requests` response. To handle this: 1. Implement exponential backoff 2. Monitor your request rate 3. Cache responses when possible 4. Batch requests when appropriate ## Best Practices * Monitor your API usage * Implement proper error handling * Use caching strategies * Consider request batching * Implement retry logic with backoff ## Need Higher Limits? If you require higher rate limits, please contact our support team with: * Your use case * Expected request volume * Required limits * Justification for the increase # Matters Source: https://docs.smokeball.com/docs/api-docs/voyymdyjn5yp3-matters Create and update Matters ## Matter Types Smokeball comes with a range of pre-configured matter types for different areas of law. These are provided by Smokeball’s internal content team and are not user editable. A Matter Type defines what information can be saved to the matter, relevant to that area of law and state / location. Matter Types When creating a matter in Smokeball we can see 1. A list of Matter Types. Here, *Financing* is a matter type, under the *Real Estate* category. 2. Representative options for the *Financing* matter type. This specifies who the Smokeball firm is acting for. In other words, who is their client. ### Getting a Matter Type ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/mattertypes" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/mattertypes", "query": { "Location": "IL", } } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/mattertypes", "offset": 0, "limit": 500, "size": 18, "first": { "href": "https://api.smokeball.com/mattertypes" }, "value": [ { "href": "https://api.smokeball.com/mattertypes/8aca3574-aecb-4a6f-991d-680861bece1d_il", "id": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "versionId": "32e86c38-534d-4362-95a1-f5c287a8d4b7", "name": "Financing", "category": "Real Estate", "representativeOptions": [ "Borrower", "Lender", "MortgageBroker", "Seller", "CooperativeApartment" ], "location": "IL", "deleted": false }, { "href": "https://api.smokeball.com/mattertypes/2d5aca9b-9469-44d4-8616-766a864432aa", "id": "2d5aca9b-9469-44d4-8616-766a864432aa", "versionId": "1410cfea-723e-4514-8901-c92dab1bd5f2", "name": "Buy", "category": "Real Estate", "representativeOptions": [ "Buyer" ], "location": "IL", "deleted": false }, ... ] } ``` > Here the important information to note is the `id` and `representativeOptions` of the matter type. We’ll need those when creating a matter. *** ## Contacts In order to successfully create a matter, we’re going to need to provide one or more contacts that are clients and/or other-sides (if applicable). In Smokeball’s desktop app you can create a contact as a Person, Firm/Business/Organization or Trust. Currently, the API supports the following contact types * Person * Company New Contact ### Creating contacts To create a client contact you can make the following request which would create a person called ‘Test Contact’. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/contacts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{"person":{"firstName":"Test","lastName":"Contact","email":"test@gmail.com"}}' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/contacts", "headers": { "Content-Type": "application/json" }, "body": "{\n \"person\": {\n \"firstName\": \"Test\",\n \"lastName\": \"Contact\", \n \"email\": \"test@gmail.com\",\n }\n}\n\n" } ``` To create an other-side contact you can make the following request which would create a person called ‘Other Contact’. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/contacts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{"person":{"firstName":"Other","lastName":"Contact","email":"other@gmail.com"}}' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/contacts", "headers": { "Content-Type": "application/json" }, "body": "{\n \"person\": {\n \"firstName\": \"Other\",\n \"lastName\": \"Contact\", \n \"email\": \"other@gmail.com\",\n }\n}\n\n" } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "href": "https://api.smokeball.com/contacts/9a9ce552-6bee-45e4-8eb1-afe2c18c489f" } ``` Take note of the Contact’s `id` returned in the response, we’ll need that when creating a matter. Alternatively, if the contact already exists in Smokeball, you can query it to find it’s id. > ### *Tip* > > When creating a contact you can pass in an `externalSystemId` if you want to store your own reference id with this contact. *** ## Staff In Smokeball, staff can be assigned to a lead as the **Attorney Responsible** or the **Person Assisting**. The lead list can then be filtered by Attorney responsible, etc. Staff If you want to specify the Attorney Responsible or Person Assisting when creating a lead via the API, then we will first need to retrieve the staff member's id. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/staff" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/staff" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/staff", "size": 28, "first": { "href": "https://api.smokeball.com/staff" }, "value": [ { "href": "https://api.smokeball.com/staff/66f7fa24-bb03-4d89-ac28-e19620cc1e45", "id": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "versionId": "cfdbb798-1f2e-4355-87bc-620cbc62acc2", "firstName": "Test", "lastName": "Staff", "phone": { "number": "200518540309" }, "cell": {}, "email": "teststaff@gmail.com", "avatar": "https://toolbar.smokeball.com/FirmManagement/Firm/RedirectToStaffProfileImageUrl?accountId=2f3143de-2bfe-4742-9e9c-7c16b48bdef7&staffId=66f7fa24-bb03-4d89-ac28-e19620cc1e45", "former": false, "enabled": true }, { "href": "https://api.smokeball.com/staff/c85d28cb-a760-4627-aa59-0a853c2e65ed", "id": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "versionId": "23dbdc50-6171-4408-8150-413cb293a249", "title": "", "firstName": "Hunter", "lastName": "Steele", "email": "hunter@gmail.com", "avatar": "https://toolbar.smokeball.com/FirmManagement/Firm/RedirectToStaffProfileImageUrl?accountId=2f3143de-2bfe-4742-9e9c-7c16b48bdef7&staffId=c85d28cb-a760-4627-aa59-0a853c2e65ed", "former": false, "enabled": false }, ... ] } ``` *** ## Creating a matter Now we have everything we need to create a matter. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{"number":"Matter-001","matterTypeId":"8aca3574-aecb-4a6f-991d-680861bece1d_IL","clientIds":["9a9ce552-6bee-45e4-8eb1-afe2c18c489f"],"otherSidesIds":["52f551d9-6c1c-44d1-8bf7-ddce5a856134"],"clientRole":"Borrower","otherSideRole":"Lender","description":"Created via API","status":"Open","dateOpened":"2020-05-20T04:20:55.035Z","personResponsibleStaffId":"c85d28cb-a760-4627-aa59-0a853c2e65ed","personAssistingStaffId":"66f7fa24-bb03-4d89-ac28-e19620cc1e45","isLead":false}' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/matters", "headers": { "Content-Type": "application/json" }, "body": { "number": "Matter-001", "matterTypeId": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "clientIds": [ "9a9ce552-6bee-45e4-8eb1-afe2c18c489f" ], "otherSidesIds": [ "52f551d9-6c1c-44d1-8bf7-ddce5a856134" ], "clientRole": "Borrower", "otherSideRole": "Lender", "description": "Created via API", "status": "Open", "dateOpened": "2020-05-20T04:20:55.035Z", "personResponsibleStaffId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "personAssistingStaffId": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "isLead": false } } ``` Bearer token for user authentication. Format: `Bearer YOUR_ACCESS_TOKEN` Must be `application/json` for this endpoint. The matter number. Can be left blank. The matter type identifier. Example: `8aca3574-aecb-4a6f-991d-680861bece1d_IL` (the id we found earlier). Contact Ids associated with the matter. Minimum: 1 client required. Example: `["9a9ce552-6bee-45e4-8eb1-afe2c18c489f"]` The client's role in the Matter (e.g., "Borrower", "Lender"). If left blank, the default role associated to the matter type will be used. Example: `"Borrower"` The other side's role in the Matter (e.g., "Borrower", "Lender"). Example: `"Borrower"` Description of the matter. Example: `"Created via API"` Initial matter status (typically "Open"). Can be `Open`, `Pending`, `Closed`, `Deleted` or `Cancelled` Example: `"Open"` Lead creation date in ISO 8601 format (this can be backdated). Example: `"2020-05-20T04:20:55.035Z"` Staff Id of the primary responsible staff member (optional). Example: `"c85d28cb-a760-4627-aa59-0a853c2e65ed"` Staff Id of the assisting staff member (optional). Example: `"66f7fa24-bb03-4d89-ac28-e19620cc1e45"` Whether the matter is marked as a lead (default: false). Example: `true` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "51cefb72-6247-4ca2-8926-5d14d65f7cb9", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" } ``` The matter is now shown in Smokeball: New Matter *** ## Retrieving matters ### Retrieving a matter ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" } ``` This is an enhanced version of the endpoint which combines the following APIs: * Matters API * MatterItems API * Roles API * Relationships API **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9", "id": "51cefb72-6247-4ca2-8926-5d14d65f7cb9", "versionId": "638138489289266248", "number": "Matter-001", "matterType": { "id": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "href": "https://api.smokeball.com/mattertypes/8aca3574-aecb-4a6f-991d-680861bece1d_IL", "rel": "MatterTypes" }, "clients": [ { "id": "9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "href": "https://api.smokeball.com/contacts/9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "rel": "Contacts" } ], "otherSides": [ { "id": "52f551d9-6c1c-44d1-8bf7-ddce5a856134", "href": "https://api.smokeball.com/contacts/52f551d9-6c1c-44d1-8bf7-ddce5a856134", "rel": "Contacts" } ], "title": "Matter-001 - Smith - Sale - Jones", "description": "Created via API", "status": "Open", "personResponsible": { "id": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "href": "https://api.smokeball.com/staff/c85d28cb-a760-4627-aa59-0a853c2e65ed", "rel": "Staff" }, "personAssisting": { "id": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "href": "https://api.smokeball.com/staff/66f7fa24-bb03-4d89-ac28-e19620cc1e45", "rel": "Staff" }, "openedDate": "2023-03-08T04:18:22.3044791Z", "isLead": false, "items": { "borrowers": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "type": "role", "name": "Borrower", "contact": { "id": "9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "href": "https://api.smokeball.com.au/contacts/9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "rel": "Contacts" }, "subItems": { "solicitors": [ { "id": "4a31c76f-0952-4069-8259-840cd6106246", "type": "role", "contact": { "id": "MyFirm" }, "name": "Solicitor", } ], "relationshipDetails": [ { "id": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", "href": "https://api.smokeball.com.au/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/71b597c3-c1dd-4fe8-91c1-3c731a1e4394", "rel": "layout", "type": "layout" } ] } } ], "lenders": [ { "id": "f794090d-183e-462a-9654-930d64a5f882", "name": "Lender", "contact": { "id": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", "href": "https://api.smokeball.com.au/contacts/52f551d9-6c1c-44d1-8bf7-ddce5a856134", "rel": "Contacts" }, "subItems": { "solicitors": [ ] } } ], "marriageDetails": [ { "id": "0ea6eb6e-b078-4de3-8639-26c99ec9c32c", "href": "https://api.smokeball.com.au/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/0ea6eb6e-b078-4de3-8639-26c99ec9c32c", "rel": "layout", "type": "layout", "subItems": { "solicitors": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "type": "role", "name": "Solicitor", "contact": { "id": "ec499947-9d3d-442a-b431-05c8ee280687", "href": "https://api.smokeball.com.au/8contacts/ec499947-9d3d-442a-b431-05c8ee280687", "rel": "Contacts" } } ] } } ] } } ``` Most of this information is unchanged from what we passed in when creating the matter. You’ll notice that the clients, other-sides, staff and matter type properties have been expanded as links to resources with more information. There is also the items collection which we’ll explore in more detail next. ### Retrieving multiple matters This endpoint can be used to sort, filter and retrieve one or more matters. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters" } ``` The endpoint returns a paginated list of matters based on the action/filter paramaters specified in the request. Use this to limit number of records to be returned. Defaults to 500. Use this to set offset for the records to be returned. Use this to filter by matter type identifier. Use this to filter by the contact identifier. Use this to filter by current status of the matter. Possible values: `Open`, `Pending`, `Closed`, `Deleted` or `Cancelled` Boolean flag to restrict search to `Leads`. Use this to filter by matters updated since a specified time (.net ticks representation of the UTC datetime). Use this to provide comma separated list of sort fields. Expected format `asc/desc(field)`. Sort fields currently supported: `Status`, `LastUpdated` - e.g. `asc(Status)`, `desc(LastUpdated)` Use this to provide comma separated list of optional fields to include in the result. Optional fields currently supported: `Items`. > The matter `items` field is not returned by default. Use the `fields` in the request to include this in the result. **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "href": "https://api.smokeball.com.au/matters", "offset": 0, "limit": 100, "size": 1396, "first": { "href": "https://api.smokeball.com.au/matters?limit=100", "rel": "collection" }, "next": { "href": "https://api.smokeball.com.au/matters?limit=100&offset=100", "rel": "collection" }, "last": { "href": "https://api.smokeball.com.au/matters?limit=100&offset=1300", "rel": "collection" }, "value": [ { "href": "https://api.smokeball.com.au/matters/04db8d78-f222-42de-b3d6-10809642402e", "id": "04db8d78-f222-42de-b3d6-10809642402e", "versionId": "638193848837974931", "title": "Company Incorporation", "matterType": { "name": "Company Incorporation", "id": "0d157e91-f3e1-4161-ae7d-03e9c23be912_NSW", "href": "https://api.smokeball.com.au/mattertypes/0d157e91-f3e1-4161-ae7d-03e9c23be912_NSW", "rel": "MatterTypes" }, "clients": [], "otherSides": [], "status": "Open", "openedDate": "2017-08-09T22:01:27.373097Z", "isLead": false, "clientIds": [], "otherSideIds": [], "items": {} }, ... ... ] } ``` *** ## Matter Items A matter can contain various items and sub items that store hierarchical data relevant to that area of law. Matter Items The matter items list is ordered as follows: * Matter Info * Borrower (Client) * Client’s Attorney - this is always the Smokeball firm. This relationship is hidden in the desktop UI but can be seen in the API. * Matter Type * Lender (Other Side) * Other Side’s Attorney * Other Roles, Relationships and Layouts Matter sub items are shown indented under the matter item. Here `Lender` is a matter item, and `Attorney` and `Loan Details` are its subitems. Here's an example request to GET the Matter Items: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/items" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/items" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "versionId": "9843e3cf-9720-4ac5-94cf-1b265c5e54d5", "items": [ { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/info", "name": "Matter Info", "index": 0, "visible": true }, { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/441225f5-0803-412b-be45-c81fb893231f", "id": "441225f5-0803-412b-be45-c81fb893231f", "name": "Borrower", "index": 0, "visible": true, "subItems": [ { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/441225f5-0803-412b-be45-c81fb893231f/relationships/04f40670-898c-4e47-9565-df8cca2569c8", "id": "04f40670-898c-4e47-9565-df8cca2569c8", "name": "Attorney", "index": 0, "visible": true } ] }, { "href": "/mattertypes/8aca3574-aecb-4a6f-991d-680861bece1d", "name": "Matter Type", "index": 0, "visible": true }, { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/e8ea6745-399b-4234-8d62-5dd891e0ae9d", "id": "e8ea6745-399b-4234-8d62-5dd891e0ae9d", "name": "Co-Counsel", "index": 0, "visible": false }, { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "id": "f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "name": "Lender", "index": 0, "visible": true, "subItems": [ { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1/relationships/31b6e000-9f39-47d4-8ee8-9fed5f6901f0", "id": "31b6e000-9f39-47d4-8ee8-9fed5f6901f0", "name": "Attorney", "index": 0, "visible": true }, { "name": "Loan Details", "index": 0, "visible": true } ] }, { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/429371ae-a577-40a0-bf4d-ed157764ed6c", "id": "429371ae-a577-40a0-bf4d-ed157764ed6c", "name": "Mortgage Broker", "index": 0, "visible": true, "subItems": [ { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/429371ae-a577-40a0-bf4d-ed157764ed6c/relationships/bbf42ca4-cf70-48e2-a737-86e1f196ebc2", "id": "bbf42ca4-cf70-48e2-a737-86e1f196ebc2", "name": "Attorney", "index": 0, "visible": true } ] }, ... } ``` Where available, the matter item will have a link to the relevant resource containing additional information. > Some matter items can be hidden on the matter by default. This is usually done for roles that aren’t common on the matter. They are still exposed in the api as "visible: false”. Currently the visibility can only be changed in the Smokeball desktop app under Matter Settings (1). Matter Roles There are different types of matter items and sub items, including: #### Roles and Relationships A contact on a matter will be assigned to a specific role or relationship, based on how they relate to the matter. For example, in the above image, `Test Contact` is assigned to the `Borrower` role **(2)**. By convention, the first Role in the list is the firm’s client. The client role has an `Attorney` relationship that links to the Smokeball Firm (hidden in the Smokeball UI) Contacts can also be related to other contacts on the matter. For example in the above image, `Lender` is a Role with an `Attorney` relationship **(3)**. This attorney acts for this specific Lender. There could be other Lenders relationships on this matter that are related to other roles (not shown in image) Roles and relationships can be set in the API. #### Layouts For a comprehensive guide to working with layouts including all field types, CRUD operations, and best practices, see the [Layouts tutorial](/docs/api-docs/0d3adba1494ce-layouts). Layouts are data entry screens that are tailored to the area of law. In this example there is a `Loan Details` layout **(4)** that would store information about that Lender's loan. A layout can either be a sub item linked to a matter item (e.g. Loan Details as shown here) or a matter item itself (e.g. Property Details, not shown). Following is a typical flow to get/set layout data. 1. Get the layout design ID and currently set data by retrieving all the layout matter items ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts" } ``` or a single layout matter item. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1" } ``` Example response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "id": "60efd655-cd1a-42cd-a170-a228cc09b5b1", "href": "https://api.smokeball.com/matters/3f0cfc3d-be5d-4a70-88ac-e099a800d46d/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1", "itemId": "60efd655-cd1a-42cd-a170-a228cc09b5b1", "index": 0, "layoutDesign": { "id": "f2654144-ab55-45ec-9852-489a5caaa111", "href": "https://api.smokeball.com/layouts/f2654144-ab55-45ec-9852-489a5caaa111", "rel": "Layouts" }, "values": [], "events": [] } ``` 2. Retrieve the full list of fields for the given layout design. ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/layouts/f2654144-ab55-45ec-9852-489a5caaa111" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/layouts/f2654144-ab55-45ec-9852-489a5caaa111" } ``` Example response: ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "id": "f2654144-ab55-45ec-9852-489a5caaa111", "href": "https://api.smokeball.com/layouts/f2654144-ab55-45ec-9852-489a5caaa111", "fields": [ { "name": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/Note", "type": "Text" }, { "name": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/OfferDate", "type": "DateTime" } ], "name": "Settlement Negotiations - Personal Injury" } ``` 3. Update the layout with new data values. ```bash curl theme={"dark"} curl --request PATCH "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "values": [ { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/Note", "value": "New note" } ] }' ``` ```json http theme={"dark"} { "method": "patch", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/layouts/60efd655-cd1a-42cd-a170-a228cc09b5b1", "headers": { "Content-Type": "application/json" }, "body": { "values": [ { "key": "Matter/SettlementNegotiations/SettlementNegotiationsDetails/Note", "value": "New note" } ] } } ``` > A field with type `Role` expects an ID of a role. The name of the role needs to be the same as the layout field (e.g. for a field with key `Matter/CaseDetails/StandardCaseDetails/Judge` expected role name is `Judge`). > A field with type `DateTime` expects a UTC date/time in [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) format (e.g. `2021-10-12T13:00:00.0000000Z`). *** ## Roles To find out which contact is assigned to a role, we can get the role details: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/441225f5-0803-412b-be45-c81fb893231f" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/441225f5-0803-412b-be45-c81fb893231f" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "id": "441225f5-0803-412b-be45-c81fb893231f", "name": "Borrower", "contact": { "id": "9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "href": "/contact/9a9ce552-6bee-45e4-8eb1-afe2c18c489f", "rel": "Contact" }, "representatives": [], "relationships": [ { "id": "04f40670-898c-4e47-9565-df8cca2569c8", "name": "Attorney", "contact": { "href": "/firm/", "rel": "Firm" }, "representatives": [] } ] } ``` Here we can see the Borrower role has a contact assigned, and we can follow the link to the contact resource to find out more details. We can also see that there is an Attorney relationship that links to the Firm resource. #### Setting a Role In our example, the Lender role did not have a contact assigned. To set this, we first need to know the id / link to the role we want to update. This was retrieved earlier with the MatterItems api call: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/items" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/items" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "versionId": "9843e3cf-9720-4ac5-94cf-1b265c5e54d5", "items": [ ... { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "id": "f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "name": "Lender", "index": 0, "visible": true, "subItems": [ { "href": "/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1/relationships/31b6e000-9f39-47d4-8ee8-9fed5f6901f0", "id": "31b6e000-9f39-47d4-8ee8-9fed5f6901f0", "name": "Attorney", "index": 0, "visible": true }, { "name": "Loan Details", "index": 0, "visible": true } ] }, ... } ``` Let's say we've already created a contact called 'ABC Lender' with id ` dcafdb9e-e46d-4720-a3f8-638b3dd4ca38` and now we want to assign it to this matter in the Lender role ```bash curl theme={"dark"} curl --request PUT "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "contactId": "dcafdb9e-e46d-4720-a3f8-638b3dd4ca38" }' ``` ```json http theme={"dark"} { "method": "PUT", "url": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "headers": { "Content-Type": "application/json" }, "body": { "contactId": "dcafdb9e-e46d-4720-a3f8-638b3dd4ca38" } } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "f377da1c-8f48-40cd-8d23-0c3b9483c6b1", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9/roles/f377da1c-8f48-40cd-8d23-0c3b9483c6b1" } ``` Roles *** ## Updating a matter There are two ways to update a matter. ### Using PUT endpoint This endpoint is similar to the endpoint used to create a new matter. ```bash curl theme={"dark"} curl --request PUT "https://api.smokeball.com/matters/{matterId}" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "number": "Matter-001", "matterTypeId": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "clientIds": [ "9a9ce552-6bee-45e4-8eb1-afe2c18c489f" ], "otherSidesIds": [ "52f551d9-6c1c-44d1-8bf7-ddce5a856134" ] }' ``` ```json http theme={"dark"} { "method": "PUT", "url": "https://api.smokeball.com/matters/{matterId}", "headers": { "Content-Type": "application/json" }, "body": { "number": "Matter-001", "matterTypeId": "8aca3574-aecb-4a6f-991d-680861bece1d_IL", "clientIds": [ "9a9ce552-6bee-45e4-8eb1-afe2c18c489f" ], "otherSidesIds": [ "52f551d9-6c1c-44d1-8bf7-ddce5a856134" ], "clientRole": "Borrower", "otherSideRole": "Lender", "description": "Updated via API", "status": "Open", "dateOpened": "2020-05-20T04:20:55.035Z", "personResponsibleStaffId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "personAssistingStaffId": "66f7fa24-bb03-4d89-ac28-e19620cc1e45", "isLead": false } } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "51cefb72-6247-4ca2-8926-5d14d65f7cb9", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" } ``` Limitations: * The `matterTypeId` field can only be modified when converting a `Lead` to a `Matter`. * The `clientIds` field cannot be modified. * The `otherSideIds` field cannot be modified. * The `Matter` cannot be converted back to `Lead`. ### Using PATCH endpoint This endpoint can be used to update specific properties and also supports updating matter items, roles and relationships. ```bash curl theme={"dark"} curl --request PATCH "https://api.smokeball.com/matters/{matterId}" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data-raw '{ "externalSystemId": "EXT123", "number": "AM-API-001", "matterTypeId": "0623643a-48a4-41d7-8c91-d35915b291cd_NSW", "description": "Matter description", "status": "Open", "openedDate": "2022-04-23T14:00:00Z", "personResponsibleStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "personAssistingStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "originatingStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "supervisorStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "clientCode": "Client A", "branchId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "branchProviderId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "clientRole": "Buyer", "isLead": false, "clientIds": [ "5bc557e8-2a14-4c23-8d35-4f0fd1bff40e" ], "otherSideIds": [ "9aec5a99-be4d-441a-80c3-21dcb549dd5a" ], "items": { "vendors": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "contact": { "id": "ec499947-9d3d-442a-b431-05c8ee280687", }, "subItems": { "solicitors": [ { "id": "4a31c76f-0952-4069-8259-840cd6106246", "contact": { "id": "MyFirm" } } ], "relationshipDetails": [ { "id": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", } ] } } ] } }' ``` ```json http theme={"dark"} { "method": "PATCH", "url": "https://api.smokeball.com/matters/{matterId}", "headers": { "Content-Type": "application/json" }, "body": { "externalSystemId": "EXT123", "number": "AM-API-001", "matterTypeId": "0623643a-48a4-41d7-8c91-d35915b291cd_NSW", "description": "Matter description", "status": "Open", "openedDate": "2022-04-23T14:00:00Z", "personResponsibleStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "personAssistingStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "originatingStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "supervisorStaffId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "clientCode": "Client A", "branchId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "branchProviderId": "c85d28cb-a760-4627-aa59-0a853c2e65ed", "clientRole": "Buyer", "isLead": false, "clientIds": [ "5bc557e8-2a14-4c23-8d35-4f0fd1bff40e" ], "otherSideIds": [ "9aec5a99-be4d-441a-80c3-21dcb549dd5a" ], "items": { "vendors": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "contact": { "id": "ec499947-9d3d-442a-b431-05c8ee280687", }, "subItems": { "solicitors": [ { "id": "4a31c76f-0952-4069-8259-840cd6106246", "contact": { "id": "MyFirm" } } ], "relationshipDetails": [ { "id": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", } ] } } ] } } } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 ACCEPTED Content-Type: application/json { "id": "51cefb72-6247-4ca2-8926-5d14d65f7cb9", "href": "https://api.smokeball.com/matters/51cefb72-6247-4ca2-8926-5d14d65f7cb9" } ``` #### Rule and limitations The endpoint supports the following actions: * Updating one or more supported properties (keeping in mind the other rules that follow). * Converting a lead to a matter - by setting `isLead: true -> isLead: false`. * Updating `matterTypeId` and `clientRole` when a lead is being converted to matter. * Updating the clients and/or other sides using the `clientIds` and `otherSideIds` properties. * Updating matter items of provider type `role` and `layout` using the `items` property. The endpoint **does not** support the following actions: * Converting a `Matter` back to a `Lead` is not supported. * Updating `matterTypeId` and `clientRole` on matters or on leads that are not being converted to matter is not supported. * Updating `items` in a patch request where a lead is being converted to a matter is not supported. * Updating `items` in a patch request where clients and/or other sides are being patched via `clientIds` and `otherSideIds` properties is not supported. * Updating matter `items` of provider type other than `role` or `layout` is not supported. * Removing all items of a type is not supported. How updating `items` works: * If the `items` property is not passed in, they will be left unchanged. * There is **no need** to pass items of all types in a patch request. * If one of more items of a type are being added/updated in the patch request, we must include all relevant items under that type (i.e., including the ones that are unchanged) in order to preserve them. Any existing item that is **not included** will be removed. * The consumer **cannot update** the name on an item, e.g., the Vendor name. * Contact Id **must be provided** when adding/update Items of role provider type. * A new item/sub item of a 'type' can only be added if there is an existing item/sub item of that type already present in the lead/matter. *Also see examples in the usage scenarios section below.* #### Usage scenarios Common scenarios are described below with clear examples. ##### Patch request to update the status and description ```json theme={"dark"} { "status": "Closed", "description": "Hectic Matter updated" } ``` ##### Patch request to convert lead to matter ```json theme={"dark"} { "matterTypeId": "16052e57-8a05-44ca-b22f-4adf96132095_NSW", "clientRole": "Client", "isLead": false } ``` ##### Patch request to update the client There are 2 ways we could do this. The first is to simply set the clientIds property, like so: ```json theme={"dark"} { "clientIds": [ "9aec5a99-be4d-441a-80c3-21dcb549dd5a" ], } ``` Alternatively, we can update the client in the items property, like so: ```json theme={"dark"} { "items": { "purchasers": [ { "id": "f794090d-183e-462a-9654-930d64a5f882", // Contact Id can be provided in one of two ways: // Option 1: "contactId": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", // Option 2: "contact": { "id": "71b597c3-c1dd-4fe8-91c1-3c731a1e4394", } // Note: As subitems are omitted here, they will not be updated } ] } } ``` ##### Patch request to add a new client/item There are 2 ways we could do this. The first is to simply set the clientIds property, like so: ```json theme={"dark"} { "clientIds": [ // Existing client must be provided here or it will be registered as a removal "9aec5a99-be4d-441a-80c3-21dcb549dd5a", // Existing client "728cb5aa-a9d2-46e5-b75a-149f6c4fae0b" // New client ], } ``` Alternatively, we can add the role in the items property, like so: ```json theme={"dark"} { "items": { "vendors": [ // The existing vendors must be here or it will be registered as a removal { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "contactId": "9aec5a99-be4d-441a-80c3-21dcb549dd5a" }, { // Id is not required here if there is no existing vendors role item available // Id must be provided if the contact is being assigned to an existing role item "contactId": "728cb5aa-a9d2-46e5-b75a-149f6c4fae0b" } ] } } ``` ##### Patch request to remove a client/item There are 2 ways we could do this. The first is to simply set the clientIds property where the missing items are removed, like so: ```json theme={"dark"} { // Assuming there were two clients here: // This patch request will remove the client who's Id was omitted here "clientIds": [ "728cb5aa-a9d2-46e5-b75a-149f6c4fae0b" ], } ``` The client/item we are removing is omitted from the payload: ```json theme={"dark"} { "items": { // Assuming there were two vendors here: // This patch request will remove the vendor item that was omitted "vendors": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "contactId": "9aec5a99-be4d-441a-80c3-21dcb549dd5a" } ] } } ``` ##### Patch request to update contact id on an existing item ```json theme={"dark"} { "items": { "purchasers": [ { "id": "5c65358d-1502-463c-9464-f36f21c7e610", "contactId": "c555185b-4062-4e74-9ecf-f0b87dd4ee79", // Contact Id can also be set this way (as mentioned in a previous example) "contact": { "id": "c555185b-4062-4e74-9ecf-f0b87dd4ee79", }, "subItems": { "Solicitor": [ { "id": "e46248a6-f71a-4991-b791-9f03e402a937", // Contact Id also updated for sub item here "contact": { "id": "9aec5a99-be4d-441a-80c3-21dcb549dd5a" } } ] } } ] } } ``` ##### Patch request to add a new layout item ```json theme={"dark"} { "items": { "Client": [ { "id": "32663bf8-7e12-4b34-8ea4-596ff9350d2d", "contact": { "id": "77b73607-761a-43a0-bdaa-a43e4568011d" }, "subItems": { "Details": [ { "id": "5dd582e1-222b-4cf1-b0d7-1daf70d3627a" }, // New `Details` sub item added here { } ] } } ], "Case Details": [ { "id": "52f51059-2a0c-4b5e-a86d-55bf5638a10d" }, // New 'Case details' item added here { } ] } } ``` ##### Patch request to remove an existing layout item ```json theme={"dark"} { "items": { // Assuming there were two Case Details layout items: // This patch request will remove the one that was omitted "Case Details": [ { "id": "52f51059-2a0c-4b5e-a86d-55bf5638a10d" } ] } } ``` # Files and Folders Source: https://docs.smokeball.com/docs/api-docs/wivbkstcwngb5-files-folders Create, add and update files and folders ## Structure Every Matter has a familiar folder structure used to store files and documents: Files If you want to work with the folder/file structure, then you will need to use the GET folders endpoint. Alternatively, if you prefer to work with a flat list of all Files on the Matter, you can use the GET files endpoint. The root folder of a matter will always have an **id** of `null` and if no **folder id** is specified when adding a File, this is the folder that will be used by default. Every folder including the root folder, will have a a number of sub-folders and files. When retrieveing a folder from the Folders endpoint you will get a list of the sub-folders and files in that folder. Here is a response for root folder of the Matter shown above: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders" } ``` \*\* Response \*\* ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "value": [ { "folders": [ { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders/27e52eaf-fa51-4582-bd59-ec3ea5ab664b", "id": "27e52eaf-fa51-4582-bd59-ec3ea5ab664b", "name": "Correspondence" }, { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders/c2f7a537-4052-4bab-a4fb-84e0d7e7cef5", "id": "c2f7a537-4052-4bab-a4fb-84e0d7e7cef5", "name": "Contracts" }, { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders/6f447d01-5379-493b-a60d-25fb4d64ff8a", "id": "6f447d01-5379-493b-a60d-25fb4d64ff8a", "name": "Emails" } ], "files": [ { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/95df25eb-dc99-45dd-881f-4608e00f551b", "id": "95df25eb-dc99-45dd-881f-4608e00f551b", "folder": {}, "name": "1. Agenda", "fileExtension": ".docx", "ownerId": "e3f545b4-34cf-49e5-b3e2-55ab0876a84c", "dateCreated": "2017-02-20T05:50:30.1568284Z", "sizeBytes": 44775, "downloadInfo": { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/95df25eb-dc99-45dd-881f-4608e00f551b/download" } }, { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/6fd46dfa-a46d-4de4-9553-dae30f7f523a", "id": "6fd46dfa-a46d-4de4-9553-dae30f7f523a", "folder": {}, "name": "Engagement Letter", "fileExtension": ".docx", "ownerId": "e3f545b4-34cf-49e5-b3e2-55ab0876a84c", "dateCreated": "2020-05-22T01:36:17.4355416Z", "sizeBytes": 20793, "downloadInfo": { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/6fd46dfa-a46d-4de4-9553-dae30f7f523a/download" } } ] } ] } ``` *** ## Creating a Folder You can create new folders within a matter to organize your files. Creating a folder is done by making a POST request to the folders endpoint. ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data '{ "name": "Legal Documents", "parentFolderId": "27e52eaf-fa51-4582-bd59-ec3ea5ab664b" }' ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders", "headers": { "Content-Type": "application/json" }, "body": { "name": "Legal Documents", "parentFolderId": "27e52eaf-fa51-4582-bd59-ec3ea5ab664b" } } ``` **Response** ```json theme={"dark"} HTTP/1.1 202 Accepted Content-Type: application/json { "href": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders/new-folder-id" } ``` ### Request Parameters * **name** (required): The name of the folder to create. Must be between 1-256 characters. * **parentFolderId** (optional): The unique identifier of the parent folder where the new folder should be created. * **userId** (optional): The unique identifier of the user creating the folder. If not provided, defaults to the authenticated user. ### Parent Folder Behavior * **Root Folder Creation**: When `parentFolderId` is `null` or not provided, the folder will be created in the root directory of the matter. * **Subfolder Creation**: When `parentFolderId` is specified with a valid folder ID, the new folder will be created as a subdirectory of that parent folder. ### Duplicate Folder Names The API allows multiple folders with the same name to exist within the same parent directory. If you attempt to create a folder with a name that already exists in the target location, the API will create a new folder with the same name rather than returning an error or the existing folder. This gives you flexibility in organizing your folder structure as needed. ### Example: Creating a Root Folder To create a folder in the root directory, omit the `parentFolderId` or set it to `null`: ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" \ --header "Content-Type: application/json" \ --data '{ "name": "Case Files" }' ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/folders", "headers": { "Content-Type": "application/json" }, "body": { "name": "Case Files", "parentFolderId": null } } ``` *** ## Adding a File Adding a File to a Matter is a two-step process involving creating the meta-data for the File by POSTing to the files endpoint and then using the response to upload the actual file to the provided URL. Continuing with the Matter example above, if we wanted to add a File to the *Correspondence* folder we would make the following request: ```bash curl theme={"dark"} curl --request POST "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "post", "url": "https://api.smokeball.com/matters/{matterId}/documents/files", "headers": { "Content-Type": "application/json" }, "body": "{\n \"fileName\": \"Letter to Creditor.docx\",\n \"folderId\": \"27e52eaf-fa51-4582-bd59-ec3ea5ab664b\",\n //\"dateCreated\": if not specified, the current datetime will be used\n}", "path": { "matterId": "5bbc4d72-3001-46bf-acd7-c90dff4dc9fa" } } ``` **Response** ```json theme={"dark"} { "fileId": "e8ac0461-6bd9-477e-a95b-1d924332c91b", "uploadUrl": "https://s3.amazonaws.com/MatterManagement.Staging/apiuploads/ODUyOGQwNjMtMjY2My00MWMwLTk0ZjQtN2JhMzNlYTRhNmQ0fGUzZjU0NWI0LTM0Y2YtNDllNS1iM2UyLTU1YWIwODc2YTg0Y3w1YmJjNGQ3Mi0zMDAxLTQ2YmYtYWNkNy1jOTBkZmY0ZGM5ZmF8MjdlNTJlYWYtZmE1MS00NTgyLWJkNTktZWMzZWE1YWI2NjRifGU4YWMwNDYxLTZiZDktNDc3ZS1hOTViLTFkOTI0MzMyYzkxYnxMZXR0ZXIgdG8gQ3JlZGl0b3IuZG9jeHw%3D?AWSAccessKeyId=ASIA364TCXVMR4RXEVO5&Expires=1590117388&x-amz-security-token=IQoJb3JpZ2luX2VjEOL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJHMEUCIFTtWItZ%2Bdjveha7XuP4a05FCTqJmEs0G4VawxdRSaQ1AiEAvi%2FCGd0kyF%2FdDugSrNbl1sMPWSOIgtD%2B4jEAHP3BPnAq3wEIOxAAGgw4MjIyNTc4MjcxNjEiDDEkwjUUF2iV7yoZVCq8AeptjefBs2u6Wn2hFiAdoPw%2F9rGsXkXGfsbyPCDKeJP2yBzpNLGsPiKZkrvrJNNY5t8eViRNqSw69qt2qH4I50Q3O5iwg8kYguCZmFRJ6Gu4g89Og5kdX5FfISdzzoz4zRyVqHUg8j1RkXvaw6o6hpBGjwyLZL24dKqtRvgylLY4faQixoZ2hWYRkT87cuxwj2BOTTt6SNpKze0Atqh6QOV2LokwpIiQo1%2Bd5Ua0nC0Xl39P4pej17W43bDmMOzUnPYFOuABSZPF2G1tnIhP%2BuLtK9HvMobUUPk5cFLcKN5QxIW2hjwlwTZF3IPsvUOVq0AO0UnJd%2By4NdfwCcpm2fUulDFl8y5wKh3%2B0BifQDfsclANHL4Bb6na0Qpp8076HWiN6kbj7HRFNW4mTtsga%2BhK4kYHwAVhS6JR9bm46bz%2FsylzvuhLnIR1eG%2BUpZLJos6LJONIn9MNK78CXY6HJxaDLaewoQ3tAhU8CF%2F6mT3Oel2ZXeY40wm3xh2aqZS%2F9nze94RpJr%2Bf2U%2FHPH8tkB8tsAjflFIA4rGX%2B0BEfqJEWWalQVo%3D&Signature=sQ%2BUezLoeweLtLhyCDK%2Fy1a6naY%3D", "expiry": "2020-05-22T03:16:28.3870695Z" } ``` You will get back the `fileId` of the new File and an `uploadUrl` that you can use to upload the actual file to AWS S3 with a PUT request. The uploadUrl will be valid up until the time specified in the `expiry` field. > For detailed instructions on uploading files using pre-signed S3 URLs, see the [AWS documentation on using presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html). The documentation covers request headers, content types, and example code in multiple languages. *** ## Uploading a new version To upload a new version of a file to an existing File on a Matter you need to get a new upload URL from the files endpoint: ```bash curl theme={"dark"} curl --request GET "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/e8ac0461-6bd9-477e-a95b-1d924332c91b/upload" \ --header "x-api-key: Y2xpZW50X2lkOmNsaWV" \ --header "Authorization: Bearer Y2xpZW50X2lkOmNsaWV" ``` ```json http theme={"dark"} { "method": "get", "url": "https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/e8ac0461-6bd9-477e-a95b-1d924332c91b/upload" } ``` **Response** ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "fileId": "e8ac0461-6bd9-477e-a95b-1d924332c91b", "uploadUrl": "https://s3.amazonaws.com/MatterManagement.Staging/apiuploads/ODUyOGQwNjMtMjY2My00MWMwLTk0ZjQtN2JhMzNlYTRhNmQ0fGUzZjU0NWI0LTM0Y2YtNDllNS1iM2UyLTU1YWIwODc2YTg0Y3w1YmJjNGQ3Mi0zMDAxLTQ2YmYtYWNkNy1jOTBkZmY0ZGM5ZmF8MjdlNTJlYWYtZmE1MS00NTgyLWJkNTktZWMzZWE1YWI2NjRifGU4YWMwNDYxLTZiZDktNDc3ZS1hOTViLTFkOTI0MzMyYzkxYnxMZXR0ZXIgdG8gQ3JlZGl0b3IuZG9jeHw%3D?AWSAccessKeyId=ASIA364TCXVMR4RXEVO5&Expires=1590117388&x-amz-security-token=IQoJb3JpZ2luX2VjEOL%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJHMEUCIFTtWItZ%2Bdjveha7XuP4a05FCTqJmEs0G4VawxdRSaQ1AiEAvi%2FCGd0kyF%2FdDugSrNbl1sMPWSOIgtD%2B4jEAHP3BPnAq3wEIOxAAGgw4MjIyNTc4MjcxNjEiDDEkwjUUF2iV7yoZVCq8AeptjefBs2u6Wn2hFiAdoPw%2F9rGsXkXGfsbyPCDKeJP2yBzpNLGsPiKZkrvrJNNY5t8eViRNqSw69qt2qH4I50Q3O5iwg8kYguCZmFRJ6Gu4g89Og5kdX5FfISdzzoz4zRyVqHUg8j1RkXvaw6o6hpBGjwyLZL24dKqtRvgylLY4faQixoZ2hWYRkT87cuxwj2BOTTt6SNpKze0Atqh6QOV2LokwpIiQo1%2Bd5Ua0nC0Xl39P4pej17W43bDmMOzUnPYFOuABSZPF2G1tnIhP%2BuLtK9HvMobUUPk5cFLcKN5QxIW2hjwlwTZF3IPsvUOVq0AO0UnJd%2By4NdfwCcpm2fUulDFl8y5wKh3%2B0BifQDfsclANHL4Bb6na0Qpp8076HWiN6kbj7HRFNW4mTtsga%2BhK4kYHwAVhS6JR9bm46bz%2FsylzvuhLnIR1eG%2BUpZLJos6LJONIn9MNK78CXY6HJxaDLaewoQ3tAhU8CF%2F6mT3Oel2ZXeY40wm3xh2aqZS%2F9nze94RpJr%2Bf2U%2FHPH8tkB8tsAjflFIA4rGX%2B0BEfqJEWWalQVo%3D&Signature=sQ%2BUezLoeweLtLhyCDK%2Fy1a6naY%3D", "expiry": "2020-05-22T03:16:28.3870695Z" } ``` You will get back an `uploadUrl` that you can use to upload the new version to AWS S3 with a PUT request. The uploadUrl will be valid up until the time specified in the `expiry` field. > For detailed instructions on uploading files using pre-signed S3 URLs, see the [AWS documentation on using presigned URLs](https://docs.aws.amazon.com/AmazonS3/latest/userguide/using-presigned-url.html). The documentation covers request headers, content types, and example code in multiple languages. *** ## Downloading a File To download a file you need to make a request to the files endpoint to get a temporary `downloadUrl`: ```html theme={"dark"} GET https://api.smokeball.com/matters/5bbc4d72-3001-46bf-acd7-c90dff4dc9fa/documents/files/95df25eb-dc99-45dd-881f-4608e00f551b/download ``` **Response** ```json theme={"dark"} { "fileId": "95df25eb-dc99-45dd-881f-4608e00f551b", "downloadUrl": "https://s3.amazonaws.com/MatterManagement.Staging/managefiles3/8528d063-2663-41c0-94f4-7ba33ea4a6d4/AC8/AC83F922D6C7912EF74A9CA025C14526?AWSAccessKeyId=ASIA364TCXVM7BWHXPON&Expires=1590130239&response-content-disposition=attachment%3B%20filename%3D%221.%20Agenda.docx%22&x-amz-security-token=IQoJb3JpZ2luX2VjEOb%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaCXVzLWVhc3QtMSJIMEYCIQCRB6GR88g1BrLlsEkCBB6Dp2VwKkWZ7mqzRZ9mQj5%2FgwIhAK%2BP28pVL4rmsvILpGq%2BM9oLwIS8Xvuy2DfbwKkZ6EXbKt8BCD8QABoMODIyMjU3ODI3MTYxIgzsM8IXTaCCGbfc90MqvAFI7iuN%2F9EVCgBTR0x4mMDoBH0beid591RJ0zsrpAGBwJ4QTYHLYzwMWRGPgAu39oDK0bUyrX5WIb6iq8G5XrBB5K0IkgEuGMSX7U0Yf8xu8QjdtusV6YbbS4hQ3L5NrDpcbZMJ12cpF3hn65hXIBSwzRMhYN4iSumxDUiGmTDNwpH83fXmvLBoPkzxUHEYlBJzLR7VqxA1G0AGQGdLNwLf29Riry84%2BpsSANNuiYh1eQSf95Rsnqc%2BiU62ZDDQyZ32BTrfAcJpfDz6Y%2Ft3GPK0sEXQHZT6%2BbnvqZC8IJFbJdkkCNQBRF5YAaE6b3NKAilCeq2gb81BQW3fEYjud9wYmRcKIpTcMAAI90JLYAkPcgNO0wTouslW5yuiqnKG%2FZZ8dCDNm%2FocBzwGRR7%2B%2Fro9sTBrT1L1%2FM%2B1eeZNiFKa45aovAZ1AnkcigFej9oIZKmkOPuEFSuP8tUFQ5IblTKxsNoipRe1LbknxF008t5x8Yb%2Bskn7jBRO6OlYzgsddaOPDD0giMjh4KwuVDAI4PQCSx1CMmq2xT9wtlG2BNHD5eT%2FKpc%3D&Signature=e0pmX3UMd%2FrZN%2FJrLswk2y7NG40%3D", "expiry": "2020-05-22T06:50:39.4054085Z", "sizeBytes": 44775 } ``` > The `downloadUrl` will be valid up until the time specified in the `expiry` field. # Webhooks Source: https://docs.smokeball.com/docs/api-docs/wivbkstcwngb5-webhooks Create and update webhook subscriptions You can use webhook subscriptions to receive notifications about particular events that occur in an account. These event notifications are sent as a HTTP POST to the callback URL that you specify in the subscription. ## Subscribing to events ### Event types Webhook subscriptions can be managed using the `/webhooks` endpoint. To see a list of available events make a GET request to `/webhooks/types` which will return a list of strings representing the events: ```json theme={"dark"} { "value": [ "activitycode.created", "activitycode.deleted", "activitycode.updated", "archive.updated", "billingconfiguration.updated", "contact.created", "contact.deleted", "contact.restored", "contact.updated", "expense.created", "expense.deleted", "expense.updated", "event.updated", "files.updated", "fee.created", "fee.deleted", "fee.updated", "firm.updated", "firmuser.updated", "invoice.finalized", "layoutdesign.updated", "layoutmatter.created", "layoutmatter.updated", "mattertype.updated", "matter.created", "matter.updated", "matteritems.updated", "matterstage.updated", "memo.created", "memo.updated", "memo.deleted", "memo.restored", "roles.updated", "staff.created", "staff.updated", "stageset.created", "stageset.updated", "task.created", "task.updated", "task.deleted", "user.registered", "user.unregistered", "error" ] } ``` The `/webhooks` endpoint will also allow you to list, edit and delete any subscriptions that you have created. ### System event types Some event types are considered system events and have special behavior: ```json theme={"dark"} "mattertype.updated" ``` **System event behavior** * System events are notified to all subscriptions regardless of the account ID. * This means you will receive these events even if they don't belong to your specific account. **Client Credentials Grant subscriptions** For integrations using Client Credentials Grant, you can create a system subscription using the special account ID `00000000-0000-0000-0000-000000000000`. This allows you to manage system events on a single Subscription without receiving notifications for all accounts. This subscription can only be used with system event types. ### Creating a subscription Requests to event notification URLs will timeout after 10 seconds. Please ensure that your event notification URL responds in time. To create a subscription, make a POST request to the `/webhooks` endpoint: ```bash curl theme={"dark"} curl --request POST 'https://api.smokeball.com/webhooks' \ --header 'Content-Type: application/json' \ --data-raw '{ "name": "MyContactsWebhook", "eventTypes": [ "contact.created", "contact.updated", "contact.deleted", "contact.restored" ], "eventNotificationUrl": "https://mydomain.com/webhooks/contacts" }' ``` ```json http theme={"dark"} { "method": "POST", "url": "https://api.smokeball.com/webhooks", "headers": { "Content-Type": "application/json" }, "body": { "name": "MyContactsWebhook", "eventTypes": [ "contact.created", "contact.updated", "contact.deleted", "contact.restored" ], "eventNotificationUrl": "https://mydomain.com/webhooks/contacts" } } ``` Response ```json theme={"dark"} HTTP/1.1 200 OK Content-Type: application/json { "id": "82336fc0-8208-406f-b11c-f2b520d081a3", "key": "i7641529ue420n8b9", "name": "MyContactsWebhook", "eventTypes": [ "contact.created", "contact.updated", "contact.deleted", "contact.restored" ], "eventNotificationUrl": "https://mydomain.com/webhooks/contacts", "createdDateUtc": "2021-05-05T01:36:43.0503643Z", "updatedDateUtc": "2021-05-05T01:36:43.0503944Z" } ``` **Effective May 30, 2025** Webhook subscriptions will be automatically removed if: * The acquired refresh token has expired. * The user or firm explicitly revokes access to the integration. **Action Required:** * Do not assume Webhook subscriptions persist after a user re-authenticates. * After obtaining a new access token, check if Webhook subscriptions exist. * If they do not exist, recreate them; otherwise, no action is needed. * **Check for existing Webhook subscriptions** before creating new ones to avoid duplicate notifications and unnecessary load on your systems. Ensure your integration follows this workflow to maintain Webhook functionality. If you have any questions, please contact **[support@smokeball.com.au](mailto:support@smokeball.com.au)**. ### Maintaining a single subscription We recommend maintaining a single webhook subscription per firm. A subscription can include as many event types as you need (edit it via the `/webhooks` endpoint to add or remove event types), so there is no need to create separate subscriptions for different events. Maintaining one subscription per firm: * Avoids duplicate notifications for the same event * Reduces load on your event notification endpoint * Makes it simpler to check for an existing subscription after a user re-authenticates (see the warning above) Before creating a subscription, list your existing subscriptions with a GET request to `/webhooks` and update the existing subscription instead of creating a new one. ## Event handling ### Notification schema Webhook notifications contain event-specific data in their `payload` field. The structure varies by event type - refer to the relevant API documentation for payload details. Below is a sample notification for a `matter.updated` event: ```json theme={"dark"} { "accountId": "1e53979d-9269-4159-b104-2e603670f59d", "userId": "526670af-acd8-401e-bc64-6d4cafb4a12a", "subscriptionId": "6543c46b-d1d5-4a00-8732-36a63a20c3b4", "type": "matter.updated", "source": "API", "payload": { "id": "68df1d38-b9a3-4855-b32f-6af1aae2f258", "externalSystemId": null, "number": "10001", "title": "John Smith | Sale", "matterType": { "id": "602b31b2-da47-4b17-9ff5-f84f3f3efeec", "rel": "MatterTypes" }, "clients": [ { "id": "6aeee3fb-7587-4b33-8570-9d34dbde5d8f", "rel": "Contacts" } ], "description": "Sale of Franchise", "status": "Open" }, "timestamp": 637981232724209300 } ``` Identifies the specific account associated with the subscription. Identifies the user that triggered the event. This may not always be present. Represents the unique identifier for the subscription. Indicates the nature of the subscribed event. Specifies the origin of the event - e.g. `Smokeball`, `API` This section holds the detailed information pertinent to the event. Captures the exact moment when the event occurred. This is useful for the subscriber to ensure events are processed in order. ### Handling duplicate events Webhook endpoints may occasionally receive the same event more than once. We advise that you guard against this by making your event processing idempotent. ### Order of events Smokeball does not guarantee delivery of events in the order in which they are generated. It is rare but possible that you may receive a `contact.updated` event before a `contact.created` event. ### Preventing infinite update loops If you initiate a change to a resource via the API you will more than likely receive one or more webhook events caused by that change. If you are not careful you might find yourself in an update loop like the following scenario: 1. A change is made to a resource in your system 2. This triggers your code to call the Smokeball API to update the corresponding resource in Smokeball 3. You receive a webhook event from Smokeball for the resource you updated 4. You update the resource in your system 5. This triggers you to call the Smokabll API to update the corresponding resource 6. and so on.... To avoid this scenario it is recommended that you set the `RequestId` header with all of you API requests. If supplied, this header will always be returned in your API responses as well as all webhook events that were triggered from your original request. This allows you to filter or ignore webhook events that were initiaited by your changes. ### Error Handling Because our API request handling is an eventually consistent operation, we supply a `error` webhook event so you can be notified when processing your API request has failed. We provide the name and id of the failed resource and a description of the problem. A sample of the `error` payload is as follows: ```json theme={"dark"} { "accountId": "1e53979d-9269-4159-b104-2e603670f59d", "subscriptionId": "6543c46b-d1d5-4a00-8732-36a63a20c3b4", "type": "error", "source": "API", "payload": { "message": "Contact does not belong to account 1e53979d-9269-4159-b104-2e603670f59d.", "resourceName": "contact", "resourceId": "fbd61c97-ee92-49b7-ba8e-84e0ed857078" }, "timestamp": 637981232724209300 } ``` ## Verifying webhooks If you supplied a `key` when you created a subscription, a header called `Signature` will be included when Smokeball calls your callback URL. The signature is a HMAC SHA256 hash that was created with the key that you provided. You can perform the same hashing process on your event processing side to verify that the request came from Smokeball. ### Computing the HMAC The `Signature` hash is calculated using these three bits of information: 1. The `Timestamp` header which is the ticks representation of the UTC datetime (.Net format) the webhook was sent 2. The `RequestId` header 3. Your `ClientId` that you use to authenticate with the API These three parameters need to be concatenated into a single string, seperated by a `|` and can then be used to create the hash. For example: ```csharp c# theme={"dark"} // Your key you provided in the subscription var key = "ei7641529ue420n8b9aa"; var timestamp = "637558795239278688"; var requestId = "38583489-09c4-49ef-b58c-ef1b34208cca"; var clientId = "lou1qnn0llav95f"; var payload = "637558795239278688|38583489-09c4-49ef-b58c-ef1b34208cca|lou1qnn0llav95"; var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)); var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload)); return BitConverter.ToString(hash).Replace("-", "").ToLower(); ``` ```javascript javascript theme={"dark"} const crypto = require('crypto'); const key = "ei7641529ue420n8b9aa"; const timestamp = "637558795239278688"; const requestId = "38583489-09c4-49ef-b58c-ef1b34208cca"; const clientId = "lou1qnn0llav95f"; const payload = `${timestamp}|${requestId}|${clientId}`; const hmac = crypto.createHmac('sha256', key); hmac.update(payload); const hash = hmac.digest('hex'); return hash; ``` This produces the hash string `feb4b838a272884f6d2c2580b2c7ebb0b2f725b90e8baa6f9b5e1a17a9faec2d` ### Guarding against replay attacks A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks Smokeball provides a `Timestamp` header which is the ticks representation (.Net format) of when the webhook event was sent. It is also part of the verification signature so an attacker can not change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you may choose to reject the message. # Base URLs Source: https://docs.smokeball.com/docs/api-docs/z2q8vla6b77g8-base-urls Base URLs for different environments and regions Refer to the below base urls for different environments and regions. You **MUST** use the URLs for the region you are targeting with your integration. ## Authentication Use this base URL for [authentication](/docs/api-docs/c916c683c136e-authentication-overview). | Region | Production | Staging/Test | | - | - | - | | US | [https://auth.smokeball.com](https://auth.smokeball.com/) | [https://datastaging-auth.smokeball.com](https://datastaging-auth.smokeball.com/) | | AU | [https://auth.smokeball.com.au](https://auth.smokeball.com.au/) | [https://datastaging-auth.smokeball.com.au](https://datastaging-auth.smokeball.com.au/) | | UK | [https://auth.smokeball.co.uk](https://auth.smokeball.co.uk/) | [https://datastaging-auth.smokeball.co.uk](https://datastaging-auth.smokeball.co.uk/) | ## API Use this base URL for [making api requests](/docs/api-docs/ivmfjbc61tvjs-making-requests). | Region | Production | Staging/Test | | - | - | - | | US | [https://api.smokeball.com](https://api.smokeball.com/) | [https://stagingapi.smokeball.com](https://stagingapi.smokeball.com/) | | AU | [https://api.smokeball.com.au](https://api.smokeball.com.au/) | [https://stagingapi.smokeball.com.au](https://stagingapi.smokeball.com.au/) | | UK | [https://api.smokeball.co.uk](https://api.smokeball.co.uk/) | [https://stagingapi.smokeball.co.uk](https://stagingapi.smokeball.co.uk/) | ## Environment Selection * Use the **Production** URLs for live applications * Use the **Staging/Test** URLs for development and testing * Ensure you're using the correct region for your target market Mixing URLs from different regions or environments may cause unexpected behavior or errors. # Common issues Source: https://docs.smokeball.com/docs/platform-docs/0l12m345k789l-common-issues Frequently encountered problems and their solutions. This guide addresses frequently encountered problems and their solutions. ## Partner Invite Password Expired If your temporary password from a partner program invitation has expired: 1. Contact our [support team](https://support.smokeball.com) to request a new invitation 2. We'll send you a fresh invite with a new temporary password 3. Use the new password to complete your account setup within 24 hours Partner invitation passwords expire after 7 days for security purposes. Be sure to set up your account promptly after receiving a new invite. ## Smokeball Login Issues If you're unable to log in to Smokeball, this is because Partner accounts and Smokeball accounts are for separate systems: * Partner accounts are used for accessing the Developer Console and Partner Portal * Smokeball accounts are firm-specific logins used to access the Smokeball application * Your partner credentials cannot be used to log into Smokeball directly If you need a Smokeball account for testing, use the [Accounts](/docs/platform-docs/9k01l234m567n-accounts) section of the Developer Console to create and manage a new account. Smokeball accounts are environment and region-specific, tied to individual firms. Your partner portal credentials won't work for logging into Smokeball directly - you need separate Smokeball account credentials. Don't try using your partner portal or developer console credentials to log in to Smokeball. These are separate systems that require different authentication. ## Missing TOTP/2FA Code If you're being prompted for a TOTP (Time-based One-Time Password) code but haven't set up 2FA: 1. Contact our [support team](https://support.smokeball.com) to request a Partner account reset 2. We'll verify your identity and reset your 2FA settings 3. You can then set up a new authenticator during your next login TOTP codes are required for all partner accounts as an additional security measure. ## OAuth Login Issues If you're encountering OAuth login issues, first verify you're using the correct authorization URL: * Check that you're using the base authorization URL as specified in our [API Documentation](/docs/api-docs/1e13a13124aee-introduction) * Ensure you're using the correct environment-specific URL (staging vs production) * Verify your redirect URI exactly matches one registered in your app settings Common OAuth login issues include: * Using incorrect environment URLs * Mismatched redirect URIs * Missing or invalid scopes * Incorrect client credentials Using the wrong base authorization URL will cause OAuth flows to fail. Always reference the official API documentation for the correct endpoints. ## Rate Limits If you're experiencing API rate limits, this is expected behavior to ensure platform stability and fair usage: * Rate limits are enforced per client ID and endpoint * Refer to our [API Documentation](/docs/api-docs/1e13a13124aee-introduction) for specific limits * Use exponential backoff when retrying rate-limited requests * Consider caching frequently accessed data Rate limits help maintain platform stability and ensure fair access for all integrations. Your app should be designed to work within these constraints. Rate limit increases are only granted in exceptional circumstances. Design your integration to work efficiently within the standard limits. ## Handling 401 Unauthorized Errors If you encounter a 401 Unauthorized error when calling the API, it typically indicates an authentication or permission issue. Here are the most common causes and how to resolve them: * **Insufficient Permissions**: The access token being used does not have the necessary permissions to access the requested resource. Ensure the authenticated user or application has the correct roles or access levels. * **Incorrect or Missing Scopes**: The token was generated without the required scopes. Double-check your app’s configuration to make sure you've requested the appropriate scopes. For more information, refer to [Building your app](/docs/platform-docs/867hiv3k5bt1u-build-app#scopes). * **Invalid Account or User ID (Server-to-Server Requests)**: If you're making server-to-server API calls, verify that the AccountId or UserId you're passing is correct and valid for your environment. ## Unable to request Access Token A common issue occurs when attempting to exchange the authorization code for an access token and receiving a `400 Bad Request` response from the token endpoint. This often happens when developers reuse an old or already-used authorization code. It is likely one of the following issues: **Authorization Code can only be used once** Each authorization code is single-use only. If the first request to exchange the code for an access token fails (even due to an invalid parameter or network issue), the code becomes invalid and **cannot be reused**. If you're not sure whether the token request succeeded, assume the code is no longer valid and initiate the flow again to get a fresh code. **Authorization Code has a short expiry** Authorization codes are time-sensitive. Most providers (including ours) only allow a few minutes between issuing the code and redeeming it for an access token. After that, the code **expires and is rejected**, even if it hasn’t been used yet. Make sure you are exchanging the code for a token as soon as it’s issued—ideally within seconds or a couple of minutes. **Recommended Steps** 1. Restart the Authorization Code flow from the beginning. 2. Authorize the app again to receive a fresh code. 3. Immediately make the token request using the new code. 4. If the request fails again, double-check: * Redirect URI matches exactly (including trailing slashes). * Client ID and Client Secret are correct. * Headers and content type are properly set (`application/x-www-form-urlencoded`). ## Adding scopes to your app If you need to add new scopes (permissions) to your app, they must first be configured and tested in the staging environment before they can be used in production. 1. Navigate to your app settings in the Developer Console and add the required scopes to your **staging** app configuration. 2. Test your app thoroughly in the staging environment to ensure the new scopes are being used correctly. 3. Once verified, click the **Publish** button to submit your app for review. This will notify our team to review the changes, including the new scopes. 4. After approval, the new scopes will be available in your production app. New scopes are not automatically available in production. They must go through the review process each time you update your app's scope requirements. For more details on the review process, see [Publishing your app](/docs/platform-docs/5g67h890f234g-publishing-app). Only request scopes that your app genuinely needs. Requesting excessive or unnecessary scopes may result in your submission being rejected during review. # Developer Console Source: https://docs.smokeball.com/docs/platform-docs/2a34b567c890d-developer-console Start working with our Developer Console The Smokeball Developer Console contains a set of self-serve tools that developers can use to manage their access to the Smokeball API and platform services. In the portal, you can: * Create and manage your Smokeball Apps (and the authentication keys and tokens that they provide) * Manage your access levels and integrations with the Smokeball API endpoints ## Quick Links * [Developer Console](https://console.smokeball.com) ## Getting Started 1. **Create an Account** * Apply to join the Smokeball Partner Program [here](https://marketplace.smokeball.com/application_forms/join-the-smokeball-partner-program/partner_applications/new) * Once approved, you'll receive access to the developer console and credentials for the staging environment. * Download Smokeball from the developer console or access the web version at: | Region | Staging | Production | | - | - | - | | US | [rc-app.smokeball.com](https://rc-app.smokeball.com) | [app.smokeball.com](https://app.smokeball.com) | | AU | [rc-app.smokeball.com.au](https://rc-app.smokeball.com.au) | [app.smokeball.com.au](https://app.smokeball.com.au) | | UK | [rc-app.smokeball.co.uk](https://rc-app.smokeball.co.uk) | [app.smokeball.co.uk](https://app.smokeball.co.uk) | 2. **Explore Resources** * Follow our [app guide](/docs/platform-docs/va20w41tid7py-get-started-overview) * Browse our [API](/docs/api-docs/1e13a13124aee-introduction) documentation * Check out our [SDK](/docs/sdk-docs/675c9816cdaf6-getting-started) documentation 3. **Start Building** * Create your first app * Integrate with our APIs * Test and deploy your solution in our staging environment ## Support Need help? We're here to assist: * [Support Portal](https://support.smokeball.com) # Apps Overview Source: https://docs.smokeball.com/docs/platform-docs/3b45c6789d012-apps-overview Understanding Smokeball apps and how they integrate with the platform ## What are Smokeball Apps? Smokeball apps are self-contained applications that extend Smokeball's functionality by integrating with firm data and workflows. Apps provide a secure, standardized way for third-party developers and firms to build custom solutions on top of the Smokeball platform. ## App Architecture ### Components A Smokeball app typically consists of: 1. **Client Application** - Your web, desktop, or mobile application 2. **API Integration** - Calls to Smokeball API endpoints to access firm data 3. **Webhook Subscriptions** (optional) - Real-time notifications for events 4. **Authentication Flow** - OAuth 2.0 implementation for secure access ### Integration Patterns Apps can integrate with Smokeball in different ways: * **API-only apps** - Cloud-based applications that interact via REST API * **Webhook-driven apps** - Event-driven integrations that respond to real-time notifications * **Plugin apps** (coming soon) - Native integrations using the Smokeball Client SDK * **Hybrid apps** - Combination of API, webhooks, and SDK ## App Types Smokeball supports two types of apps, each designed for different use cases: ### Public Apps Public apps are designed to be used by multiple Smokeball firms: * **Use Case**: SaaS products, marketplace integrations, third-party services * **Distribution**: Available through the Smokeball App Marketplace * **Credentials**: Single set of client credentials for all firms * **Authentication**: Authorization Code Grant * **Ideal For**: Software vendors, integration partners, marketplace apps **Example Use Cases:** * Document automation platforms * Client portal solutions * Legal practice management integrations * Billing and accounting integrations ### Private Apps Private apps are designed for single-firm use only: * **Use Case**: Custom internal integrations, firm-specific workflows * **Distribution**: Private to your firm only * **Credentials**: Unique credentials for your firm * **Authentication**: Client Credentials Grant or Authorization Code Grant (OAuth 2.0) * **Ideal For**: Individual firms, custom internal tools, proof-of-concept projects **Example Use Cases:** * Custom reporting dashboards * Internal automation workflows * Legacy system integrations * Data migration tools ## Authorization and Access All apps use OAuth 2.0 to securely access firm data through the Smokeball API: ### How Authorization Works 1. **App Registration** - Create an app in the [Developer Console](https://console.smokeball.com) 2. **Credentials** - Receive client ID and client secret 3. **Firm Authorization** - Firms explicitly authorize your app 4. **Access Tokens** - Obtain scoped access tokens via OAuth 2.0 5. **API Calls** - Use tokens to make authenticated API requests ### Security Features * **Scoped Permissions**: Apps request only the data they need * **Secure Authentication**: OAuth 2.0 industry standard * **Token Expiration**: Access tokens expire and must be refreshed * **Audit Trail**: All API access is logged ## App Lifecycle ### 1. Development * Create app in Developer Console * Configure authentication settings * Set up development environment * Build and test in staging environment ### 2. Testing * Test OAuth flow * Verify API integrations * Test webhook handling * Validate error handling ### 3. Deployment * **Private Apps**: Deploy and use immediately * **Public Apps**: Submit for review, then publish to marketplace ### 4. Maintenance * Monitor API usage * Handle version updates * Respond to firm support requests * Track error rates and performance ## Managing Your Apps Use the [Developer Console](https://console.smokeball.com) to: * Create and configure apps * Manage API credentials * Set OAuth redirect URIs * Configure webhook subscriptions * Monitor app usage and performance * View error logs and analytics ## Next Steps Ready to build your app? Here's how to get started: 1. **[Creating an App](/docs/platform-docs/4f56d789e123f-creating-app)** - Step-by-step guide to creating your first app 2. **[Building Your App](/docs/platform-docs/867hiv3k5bt1u-build-app)** - Development best practices and patterns 3. **[API Documentation](/docs/api-docs/1e13a13124aee-introduction)** - Complete API reference 4. **[SDK Documentation](/docs/sdk-docs/675c9816cdaf6-getting-started)** - Client SDK guides (for plugin apps) Need help deciding between Public and Private apps? Choose Private for single-firm use and simpler setup. Choose Public to distribute to multiple firms via the marketplace. # Creating an app Source: https://docs.smokeball.com/docs/platform-docs/4f56d789e123f-creating-app Learn how to create and configure a Smokeball app in the Developer Console This guide will walk you through the key considerations and requirements for developing a Smokeball App. To begin creating your App, first navigate to the Developer Console and select your target region. Then click the "New App" button to start the app creation process. This will guide you through setting up the basic configuration for your application. Partners are currently limited to creating a single app. Please contact our Partnerships team if you need to create additional apps or delete your existing app. ## App Parameters When creating an App, you will need to specify several parameters. ### App Name The App name and description help identify your app within the Developer Console and internal systems. While these are important for development and administration purposes, they are separate from your marketplace listing details which are managed through our Partnerships team. Choose a clear, descriptive name and provide a concise description that accurately represents your app's functionality. ### App Type There are important considerations to make when selecting an App Type. Smokeball supports two options: **Public App** and **Private App**. #### Choosing an App Type | App Type | Availability | Typical Use Case | Required OAuth Flow(s) | | - | - | - | - | | **Public App** | Partners only | Listed on the Smokeball marketplace and used by multiple firms | [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) | | **Private App** | Partners & Firms | Not listed on marketplace for internal use by a single firm | [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) or [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) | #### What is a **Public App**? **Public Apps** are designed to be listed and distributed in the Smokeball marketplace. They are intended for broad use across multiple firms and must meet our review and approval requirements. * Public apps must use the [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) flow for authentication. * This option is available **only to Partners**. #### What is a **Private App**? **Private Apps** are intended for private/internal use within a single firm and are not distributed through the marketplace. * Private apps can be used by firms building their own integrations, or by partners developing an app specifically for a firm's use. * Private apps can use either the [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) flow or the [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) for authentication. * The **Client Credentials Grant** is recommended for server-to-server apps tied to only one firm. * This option is available to **both Partners and Firms**. *** ### Public Client The Public Client option relates to how your application authenticates with Smokeball using OAuth 2.0. OAuth 2.0 is an industry-standard protocol for authorization that enables applications to obtain limited access to user accounts without handling their credentials directly. For applications that run in less secure environments, such as: * Mobile applications * Single-page applications (SPAs) * Browser-based applications These types of applications cannot securely store client secrets because their code is exposed to the end user. Instead of using client secrets for authentication, public clients use PKCE (Proof Key for Code Exchange) - an extension to the OAuth 2.0 protocol that provides additional security for these scenarios. PKCE works by having the client generate a unique code verifier for each authorization request, making the flow more secure even without a client secret. This enhances the standard OAuth 2.0 Authorization Code flow by preventing certain types of attacks. This is particularly important for: * Applications where the source code is publicly accessible * Environments where secure storage of secrets is challenging * Situations where the client code could be intercepted or examined If your application falls into any of these categories, you should enable the Public Client option. Private applications that run in secure server environments typically don't need this option as they can safely store and use client secrets. This setting cannot be updated later. Please contact our Partnerships team if you are unsure. ## Security The security of our customers' data is paramount. Your App must: * Meet industry security standards * Implement recommended security best practices * Complete a security risk assessment before launch ## User Experience While we maintain flexibility in design, your App should: * Complement Smokeball's existing functionality * Provide clear value to users * Maintain a consistent and intuitive interface * Follow platform UI/UX best practices ## Next Steps After creating your app, it's time to build it! * Use our API to start integrating with your application. See [API documentation](/docs/api-docs/1e13a13124aee-introduction) here. * If you integrating to Smokeball with our Plugin architecture (coming soon), you can use the Smokeball SDK to integrate. See [SDK documentation](/docs/sdk-docs/675c9816cdaf6-getting-started) here. # Publishing your app Source: https://docs.smokeball.com/docs/platform-docs/5g67h890f234g-publishing-app Publish your app to the marketplace Before publishing your App, ensure you are following best practices: ### App Name and Description Choose an appropriate name and description for your app before submitting it for review. These help users understand what your app does and help our team assess whether the listing accurately reflects its functionality. When preparing your app details: * Use a clear, descriptive app name that reflects your app's purpose * Avoid vague, overly generic, or misleading names * Write a concise description that explains the value of your app and the problem it solves * Make sure the description matches the app's actual functionality and supported workflows * Review your wording for spelling, grammar, and clarity before publishing ### Scopes Usage You must demonstrate the use of all the permissions (scopes) you are requesting within the staging environment. This helps us verify that your app is using the scopes appropriately. #### Minimize Excessive Polling * Avoid repeatedly querying our APIs at frequent intervals. Instead, leverage [webhooks](/docs/api-docs/wivbkstcwngb5-webhooks) to be notified when changes occur. * Polling should only be used when absolutely necessary, and with appropriate intervals. #### Webhook Usage * Maintain a single webhook subscription per firm. A subscription can include all the event types you need, and multiple subscriptions lead to duplicate notifications and unnecessary load. See [Maintaining a single subscription](/docs/api-docs/wivbkstcwngb5-webhooks#maintaining-a-single-subscription). * Check for existing subscriptions after a user re-authenticates and update them rather than creating new ones. #### Efficient API Usage * **Pagination**: Handle API responses with pagination to manage large datasets and prevent overload. * **Rate limiting**: Respect API rate limits by implementing retries with exponential backoff to prevent throttling. #### Error Handling * Implement robust error handling for network issues, failed API calls, and server-side errors. * Use retry mechanisms for transient errors but avoid overwhelming the server with retries. #### Security & Data Privacy * Request only the scopes you truly need for your app's functionality. Excessive permission requests might lead to rejection. * Follow OAuth2 best practices for authentication and refresh tokens properly to avoid unnecessary login requests. * Cache Client Credentials access tokens and reuse them until they expire rather than requesting a new token for every API request. See [Caching Access Tokens](/docs/api-docs/h72024p76i8e1-client-credentials-grant#caching-access-tokens). #### Testing & Monitoring * Thoroughly test your app in a staging environment that mimics production. * Set up real-time monitoring to catch issues early, and track the performance of your app in production. ## Review Process When your app has been thoroughly tested and is ready for production deployment, you can initiate the review process through the Developer Console. Navigate to your app settings and click the "Publish" button to submit your app for review. Before submitting, ensure all your changes have been saved. Any changes you make after submitting will be reflected in the review process. This will notify our team to begin the review process. Our comprehensive review process is designed to ensure all apps in the Smokeball Marketplace meet rigorous quality, security, and performance standards. Before your app can be made available to users, it must successfully complete the following review stages: 1. **Best Practices Verification** * We verify that your app follows all the best practices outlined above * This includes checking scope usage, API efficiency, error handling, and security measures * Performance and resource usage are evaluated to ensure they meet our standards 2. **Staging Environment Demo** * Contact our Partnerships team to schedule a demonstration of your app * The demo should show your app running in the staging environment * Be prepared to demonstrate all major features and functionality * Our team will verify proper implementation of requested scopes 3. **Security Assessment** * Your app's security measures will be evaluated * We'll verify proper implementation of OAuth2 flows * Data handling practices will be reviewed Only after successfully completing the review process will your app be approved for production deployment. This helps ensure that all apps in our ecosystem maintain high standards of quality and security. You can contact the Partnerships team at [partners@smokeball.com](mailto:partners@smokeball.com). ## Next Steps After your app has been published: * Monitor its performance * Gather user feedback * Plan for updates and improvements # Security Source: https://docs.smokeball.com/docs/platform-docs/6h78i901g345h-security Learn how to follow best security practices This guide covers essential security practices for handling authentication tokens and securing your app on the Smokeball platform. ## Best Practices Here are essential security practices for handling OAuth 2.0 authentication tokens in your application: ### Secure Token Storage * Never store access tokens or refresh tokens in client-side code, local storage, or cookies * Store tokens securely on your backend server using: * Encrypted databases * Key management services (AWS KMS, Azure Key Vault, etc.) * Use environment variables for sensitive credentials in development * Implement proper access controls and audit logging for token access ### Token Handling * Only transmit tokens over HTTPS/TLS * Implement token refresh using the supplied refresh token * Revoke compromised tokens immediately or contact us if you think you have been compromised ### API Key Rotation Rotate your API key regularly and immediately if you suspect it has been exposed. This helps reduce the risk of unauthorized access and limits the impact of compromised credentials. To rotate your API key, sign in to the Developer Console and navigate to the API section. Find the option to rotate your API key and follow the prompts: API key rotation screenshot placeholder ## Resources * [OAuth 2.0 Best Practices](https://oauth.net/2/) * [OAuth 2.0 Security](https://datatracker.ietf.org/doc/html/rfc6819) # Sharing your app Source: https://docs.smokeball.com/docs/platform-docs/7i89j012h456i-sharing-app Learn how to share your app with a partner > This guide is intended for firms who want to share access to their private Smokeball app with external developers or partners. If you are looking to develop marketplace apps, please refer to our [Partner Program](/docs/platform-docs/8j90k123i567j-partner-program). When developing a private app for your firm, you may want to engage an external developer or partner to build the integration while maintaining ownership of the app credentials and configuration. This allows you to: * Retain control of API access and credentials * Manage app settings and permissions * Own the relationship with Smokeball * Handle user authorization flows directly The app sharing feature enables you to safely grant a partner access to develop your app while keeping the app registered under your firm's account. ## How App Sharing Works 1. Create your app in the Developer Console 2. Navigate to the app's settings page 3. Click "Share" and enter an **existing** partner's email address 4. The partner will now be able to access the app using their developer dashboard. 5. They can develop and test the app using their own developer account The partner will have access to: * App configuration settings * API credentials for development However, they won't be able to: * Transfer ownership of the app * Delete the app * Modify critical security settings This maintains appropriate separation of concerns while enabling collaborative development. Only share your app with trusted development partners. They will have access to your app's credentials. # Building your app Source: https://docs.smokeball.com/docs/platform-docs/867hiv3k5bt1u-build-app Learn how to build apps for Smokeball as a firm or partner This guide explains how to build applications for Smokeball, with separate processes for law firms and integration partners. After creating your app, select your app from the list to view its configuration details and settings: ## Environment You can switch between the Staging and Production environments to access different configurations of your app using the tabs above your app details. Partners developing applications for multiple firms must go through our staging environment and security review process before accessing production. Firms building custom applications for their own use have direct access to the production environment. For Partners, the Production environment becomes available after your app is successfully published. Learn more in our [publishing your app](/docs/platform-docs/publishing-app) guide. ## App Name and Description The App name and description help identify your app within the Developer Console and internal systems. While these are important for development and administration purposes, they are separate from your marketplace listing details which are managed through our Partnerships team. Choose a clear, descriptive name and provide a concise description that accurately represents your app’s functionality. The app name and description are independant and not reflected in your marketplace listing. ## Scopes Scopes define the specific permissions and access levels your app requires to interact with Smokeball's APIs. When users authorize your app, they grant access only to the functionality defined by your selected scopes: * Read scopes permit GET requests * Write scopes permit POST, PUT, PATCH requests * Multiple scopes may be needed for actions on child resources (e.g. both matters/write and documents/write are needed for adding files to a matter) You should request only the minimum scopes necessary for your app's functionality to build trust with users. While you can grant any scope in Staging, they must be approved for Production use when submitting your app. Requesting unnecessary scopes may cause users to be hesitant to authorize your app. Only select scopes that are essential for your app's core functionality. ### Install URL Apps using the [Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant) show an **Installation Instructions** panel with a ready-made install URL. This is the `/oauth2/authorize` URL a user visits to authorize your app, built from the client ID, the first callback URL and the app's scopes. The URL always includes a `scope` parameter listing every scope assigned to the app, sorted alphabetically, and it updates as you add or remove scopes. Copy the URL after you have saved your scope changes so that it matches what the authorization server will accept. OAuth 2.0 expects a client to request its scopes explicitly, so the `scope` parameter is always included. If you build the URL yourself, include it too. See [Requesting scopes](/docs/api-docs/qf83nz2scopes-scopes#requesting-scopes). ## Callback URLs Callback URLs (or redirect URLs) are essential endpoints that handle the OAuth 2.0 authorization flow. They specify where users will be redirected after authorizing your application: * The authorization server redirects users here after they grant/deny permission * The URL must exactly match one registered in your app settings * Only HTTPS URLs are allowed in production (except for localhost during development) Your authorization request will fail if the redirect URI doesn't exactly match one of your registered callback URLs, including: * Protocol (http vs https) * Hostname (including subdomains) * Port number * Path and query parameters For security, we recommend: * Using dedicated endpoints for handling OAuth callbacks * [Implementing CSRF protection](#csrf-protection) * Validating state parameters * Only registering URLs you control You can add multiple callback URLs to support different environments (development, staging, production) or application components. Callback URLs are not required for apps utilising the [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) type. ## Logout URLs Logout URLs specify where users will be redirected after signing out of your application. These URLs serve several important purposes: * Allow you to properly clean up user sessions and cached data * Provide opportunity to remove sensitive information * Enable seamless user experience by redirecting to appropriate landing pages * Support proper token revocation and cleanup Configuration requirements: * The logout\_uri parameter in your /logout endpoint calls must exactly match a registered logout URL * Multiple logout URLs can be registered Your logout request will fail if the provided url doesn't exactly match one of your registered logout URLs. Callback URLs are not required for apps utilising the [Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant) type. ## Credentials Your app's OAuth 2.0 credentials consist of a client ID and client secret that are used to authenticate API requests and obtain access tokens through the OAuth 2.0 authorization flow ### Client ID and Secret The client ID uniquely identifies your application when making requests to the Smokeball API. The client secret is a confidential key used to authenticate your application: * The client ID is public and included in authorization requests * The client secret must be kept secure and never exposed in client-side code (note: client secrets are not used with PKCE/public clients) * Both are required for obtaining access tokens via OAuth flows * Contact our partners team immediately if you suspect your client secret has been compromised so it can be rotated ### Client Grant There are 2 different types of grants * **[Authorization Code Grant](/docs/api-docs/2t26gcuuqf1wk-authorization-code-grant)t**: The standard OAuth 2.0 flow for applications that can securely store client secrets. Uses PKCE (Proof Key for Code Exchange) if configured as a Public Client during app creation, otherwise requires client secrets. Provides the highest level of security. * **[Client Credentials Grant](/docs/api-docs/h72024p76i8e1-client-credentials-grant)**: For server-to-server applications that need to access resources without user context. Only supported for [Private Apps](/docs/platform-docs/4f56d789e123f-creating-app#app-type). For detailed implementation guides and examples, see our [API Documentation](/docs/api-docs). ### Access Token Access tokens are typically short-lived to enhance security, and their expiration is an essential aspect of the OAuth 2.0 security framework. The access token should be kept secure and not exposed to unauthorized parties. ### Refresh Token A refresh token is a long-lived token used to obtain a new access token once the original access token expires. It allows your Smokeball App to remain authenticated and continue using the Smokeball API. Once the refresh token expires, a new one must be acquired via the OAuth 2.0 process. The refresh token should be kept secure and not exposed to unauthorized parties. ### Token Expiry Access tokens have a default expiry of 1 hour for security. When an access token expires: * API requests will return 401 Unauthorized errors * Your application should obtain a new access token using the refresh token Refresh tokens are valid for 180 days by default. The 180-day expiry took effect on **24 August 2026** and applies to refresh tokens issued on or after that date. Refresh tokens issued before then remain valid for 30 days, so your users will get the longer expiry the next time they re-authenticate. You can request a longer refresh token expiry period when submitting your app for production approval. Include this request in the submission notes when publishing your app and our team will review based on your use case. For best practices on securely managing tokens, see our [Security](/docs/platform-docs/6h78i901g345h-security) guidelines. ## Resources ### Documentation * [API Documentation](/docs/api-docs/1e13a13124aee-introduction) * [SDK Documentation](/docs/sdk-docs/675c9816cdaf6-getting-started) * [Security Guidelines](/docs/platform-docs/6h78i901g345h-security) ### CSRF Protection Cross-Site Request Forgery (CSRF) protection is critical when implementing OAuth flows. Here are key practices to prevent CSRF attacks: #### Use State Parameter * Generate a unique, random state parameter for each authorization request * Store the state in your server-side session * Validate the returned state matches before processing the callback * Use cryptographically secure random values (at least 128 bits) ### Tools * [Developer Console](https://console.smokeball.com) * [Status](/docs/platform-docs/e8rdxqzugb5zf-status) * [Support Portal](https://support.smokeball.com) Need help? [Contact our support team](https://support.smokeball.com) # Partner Program Source: https://docs.smokeball.com/docs/platform-docs/8j90k123i567j-partner-program Join our partner program To develop integrations with Smokeball, you'll need to go through our partner approval process. This ensures we work with companies that align with our values and can provide complementary solutions to our legal practice management software. ## Step 1: Partner Application Start by applying to become a Smokeball partner. You'll need to provide: * Information about your company * Details about your proposed integration * Your target market and client base * Your distribution strategy and target regions This helps us understand how your solution complements Smokeball and benefits our mutual clients. Once approved: * You'll receive access to our developer resources * Can begin building your integration * Get support from our developer relations team ## Step 2: Security Review After you have tested your integration in our staging environment and it is submitted for review, we'll: * Ask security-related questions about your solution * Review your data handling practices * Discuss integration security measures ## Next Steps Once your integration passes our security review: * Your app will be promoted to our production environment * You can list your solution on the Smokeball Marketplace * You'll gain access to our production API endpoints and credentials * You can begin onboarding real customers The Smokeball Marketplace gives you exposure to thousands of law firms looking to enhance their practice management capabilities. ## Resources * [Developer Console](https://console.smokeball.com) * [Marketplace](https://marketplace.smokeball.com) * [Support Portal](https://support.smokeball.com) # Accounts Source: https://docs.smokeball.com/docs/platform-docs/9k01l234m567n-accounts Manage your staging and production accounts in the Developer Console Smokeball accounts differ per region (AU/US/UK) and environment (Staging/Production). You can create a new account for each environment you need to access. The Accounts page in the Developer Console allows you to manage your staging and production accounts. This page provides a centralized location to view and configure your account settings for both environments. If an account doesn't exist, you can create one using the "Create Account" button. This will guide you through the process of setting up a new account for your chosen environment. A prompt will appear to confirm the account creation details. Once created, an email will be sent to the account email address with a temporary password to set up the account. # Status Source: https://docs.smokeball.com/docs/platform-docs/e8rdxqzugb5zf-status Check the status of Smokeball services across all regions ## Regions ### United States